xref: /btstack/src/ble/gatt-service/hids_client.c (revision 0cbdb21b00702bcd005d84afc2b7996b2e2d2284)
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 
253fd39e93aSMilanka Ringwald static uint8_t find_report_index_for_report_id_and_report_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
25483d7ed1cSMilanka Ringwald     uint8_t i;
255835a13f1SMilanka Ringwald 
25683d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
257fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
258fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
259fd39e93aSMilanka Ringwald 
260fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
26183d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
26283d7ed1cSMilanka Ringwald                 continue;
26383d7ed1cSMilanka Ringwald             }
264fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
26583d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
26683d7ed1cSMilanka Ringwald                 continue;
26783d7ed1cSMilanka Ringwald             }
268fd39e93aSMilanka Ringwald         }
269fd39e93aSMilanka Ringwald 
270fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
27183d7ed1cSMilanka Ringwald             return i;
27283d7ed1cSMilanka Ringwald         }
27383d7ed1cSMilanka Ringwald     }
27483d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
27583d7ed1cSMilanka Ringwald }
27619146789SMilanka Ringwald 
277021192e1SMilanka 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){
278ab116b1cSMilanka Ringwald 
27919146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
280ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2813322b222SMilanka Ringwald         return report_index;
282ab116b1cSMilanka Ringwald     }
28328da36a6SMilanka Ringwald     report_index = client->num_reports;
284ab116b1cSMilanka Ringwald 
28528da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
28628da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
28728da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
28828da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
289ab116b1cSMilanka Ringwald 
29028da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
29128da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
29228da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
293021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
29470093cf5SMilanka Ringwald 
295021192e1SMilanka 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);
296ab116b1cSMilanka Ringwald         client->num_reports++;
2973322b222SMilanka Ringwald         return report_index;
298ab116b1cSMilanka Ringwald     } else {
299ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3003322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
301ab116b1cSMilanka Ringwald     }
302ab116b1cSMilanka Ringwald }
303ab116b1cSMilanka Ringwald 
30419146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
305556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
306556456ccSMilanka Ringwald 
307556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
30819146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
309556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
31019146789SMilanka Ringwald 
311556456ccSMilanka Ringwald         client->num_external_reports++;
31219146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
313556456ccSMilanka Ringwald         return report_index;
314556456ccSMilanka Ringwald     } else {
315556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
316556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
31770093cf5SMilanka Ringwald     }
318556456ccSMilanka Ringwald }
319556456ccSMilanka Ringwald 
3207e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3217e1e6e7dSMilanka Ringwald     uint8_t i;
3223322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3233322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
324556456ccSMilanka Ringwald             return i;
3253322b222SMilanka Ringwald         }
3263322b222SMilanka Ringwald     }
327556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
328556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3293322b222SMilanka Ringwald }
3303322b222SMilanka Ringwald 
3313322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3323322b222SMilanka Ringwald     client->service_index++;
3333322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
334556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3353322b222SMilanka Ringwald         return true;
3363322b222SMilanka Ringwald     }
3373322b222SMilanka Ringwald     return false;
3383322b222SMilanka Ringwald }
3393322b222SMilanka Ringwald 
3403322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3413322b222SMilanka Ringwald     client->service_index = 0;
3423322b222SMilanka Ringwald 
3433322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3443322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3453322b222SMilanka Ringwald         return true;
3463322b222SMilanka Ringwald     }
3473322b222SMilanka Ringwald     return false;
3483322b222SMilanka Ringwald }
3493322b222SMilanka Ringwald 
3503322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
351021192e1SMilanka Ringwald     client->report_index++;
35219146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3533322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
354e7bd2dbeSMilanka Ringwald         return true;
355e7bd2dbeSMilanka Ringwald     }
356e7bd2dbeSMilanka Ringwald     return false;
357e7bd2dbeSMilanka Ringwald }
358e7bd2dbeSMilanka Ringwald 
3593322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
360021192e1SMilanka Ringwald     client->report_index = 0;
36119146789SMilanka Ringwald     if (client->num_external_reports > 0){
3623322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3637e1e6e7dSMilanka Ringwald         return true;
3647e1e6e7dSMilanka Ringwald     }
3657e1e6e7dSMilanka Ringwald     return false;
3667e1e6e7dSMilanka Ringwald }
3677e1e6e7dSMilanka Ringwald 
3687e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3697e1e6e7dSMilanka Ringwald     uint8_t i;
3707e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
371fd39e93aSMilanka Ringwald 
372021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
37370093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
374fd39e93aSMilanka Ringwald         if (!report.boot_report){
37570093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3767e1e6e7dSMilanka Ringwald                 index = i;
377021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3787e1e6e7dSMilanka Ringwald                 break;
37970093cf5SMilanka Ringwald             }
380fd39e93aSMilanka Ringwald         }
381fd39e93aSMilanka Ringwald     }
382021192e1SMilanka Ringwald     client->report_index = index;
3837e1e6e7dSMilanka Ringwald     return index;
3847e1e6e7dSMilanka Ringwald }
3857e1e6e7dSMilanka Ringwald 
3867e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
387021192e1SMilanka Ringwald     client->report_index++;
3887e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
38928da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
390e7bd2dbeSMilanka Ringwald         return true;
39170093cf5SMilanka Ringwald     }
3927e1e6e7dSMilanka Ringwald     return false;
3937e1e6e7dSMilanka Ringwald }
3947e1e6e7dSMilanka Ringwald 
3957e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
396021192e1SMilanka Ringwald     client->report_index = 0;
3977e1e6e7dSMilanka Ringwald 
3987e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
39928da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4007e1e6e7dSMilanka Ringwald         return true;
4017e1e6e7dSMilanka Ringwald     }
402e7bd2dbeSMilanka Ringwald     return false;
40370093cf5SMilanka Ringwald }
404ab116b1cSMilanka Ringwald 
405021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
406021192e1SMilanka Ringwald     uint8_t i;
407021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
408021192e1SMilanka Ringwald 
409021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
410021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
411fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
412fd39e93aSMilanka Ringwald 
413fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
414fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
415021192e1SMilanka Ringwald                 continue;
416021192e1SMilanka Ringwald             }
417fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
418fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
419fd39e93aSMilanka Ringwald                 continue;
420fd39e93aSMilanka Ringwald             }
421fd39e93aSMilanka Ringwald         }
422fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
423021192e1SMilanka Ringwald             index = i;
424021192e1SMilanka Ringwald         }
425021192e1SMilanka Ringwald     }
426021192e1SMilanka Ringwald     client->report_index = index;
427021192e1SMilanka Ringwald     return index;
428021192e1SMilanka Ringwald }
429021192e1SMilanka Ringwald 
430021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
431021192e1SMilanka Ringwald     client->report_index++;
432021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
433021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
434021192e1SMilanka Ringwald         return true;
435021192e1SMilanka Ringwald     }
436021192e1SMilanka Ringwald     return false;
437021192e1SMilanka Ringwald }
438021192e1SMilanka Ringwald 
439021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
440cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
441cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
442cd28e7b1SMilanka Ringwald #endif
443021192e1SMilanka Ringwald     client->report_index = 0;
444021192e1SMilanka Ringwald 
445021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
446021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
447021192e1SMilanka Ringwald         return true;
448021192e1SMilanka Ringwald     }
449021192e1SMilanka Ringwald     return false;
450021192e1SMilanka Ringwald }
451021192e1SMilanka Ringwald 
452cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
453cd28e7b1SMilanka Ringwald     client->report_index++;
454cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
455cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
456cd28e7b1SMilanka Ringwald         return true;
457cd28e7b1SMilanka Ringwald     }
458cd28e7b1SMilanka Ringwald     return false;
459cd28e7b1SMilanka Ringwald }
460cd28e7b1SMilanka Ringwald 
461cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
462cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
463cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
464cd28e7b1SMilanka Ringwald #endif
465cd28e7b1SMilanka Ringwald     client->report_index = 0;
466cd28e7b1SMilanka Ringwald 
467cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
468cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
469cd28e7b1SMilanka Ringwald         return true;
470cd28e7b1SMilanka Ringwald     }
471cd28e7b1SMilanka Ringwald     return false;
472cd28e7b1SMilanka Ringwald }
473cd28e7b1SMilanka Ringwald 
474cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
475cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
476cf26c8fbSMilanka Ringwald     if (!client){
477cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
478cf26c8fbSMilanka Ringwald         return NULL;
479cf26c8fbSMilanka Ringwald     }
480cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
481cf26c8fbSMilanka Ringwald     client->cid = cid;
482cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
483fc975d0eSMilanka Ringwald 
484cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
485cf26c8fbSMilanka Ringwald     return client;
486fc975d0eSMilanka Ringwald }
487fc975d0eSMilanka Ringwald 
488cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
489021192e1SMilanka Ringwald     // stop listening
490021192e1SMilanka Ringwald     uint8_t i;
491021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
492021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
493021192e1SMilanka Ringwald     }
494021192e1SMilanka Ringwald 
495da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
496cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
497cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
498fc975d0eSMilanka Ringwald }
499cf26c8fbSMilanka Ringwald 
500cf26c8fbSMilanka Ringwald 
501cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5026bcfb631SMilanka Ringwald     uint8_t event[8];
503cf26c8fbSMilanka Ringwald     int pos = 0;
504cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
505cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
506cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
507cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
508cf26c8fbSMilanka Ringwald     pos += 2;
509cf26c8fbSMilanka Ringwald     event[pos++] = status;
510fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
511cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
512cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
513cf26c8fbSMilanka Ringwald }
514cf26c8fbSMilanka Ringwald 
515cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
516cd28e7b1SMilanka Ringwald     uint8_t event[6];
517cd28e7b1SMilanka Ringwald     int pos = 0;
518cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
519cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
520cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
521cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
522cd28e7b1SMilanka Ringwald     pos += 2;
523cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
524cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
525cd28e7b1SMilanka Ringwald }
526cd28e7b1SMilanka Ringwald 
527021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
528021192e1SMilanka Ringwald     uint16_t pos = 0;
529021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
530021192e1SMilanka Ringwald     pos++;  // skip len
531021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
532021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
533021192e1SMilanka Ringwald     pos += 2;
534021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
535021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
536021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
537021192e1SMilanka Ringwald     pos += 2;
538021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
53983d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
54083d7ed1cSMilanka Ringwald 
541021192e1SMilanka Ringwald }
542021192e1SMilanka Ringwald 
543f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
544f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
545f4d3b82aSMilanka Ringwald 
546f4d3b82aSMilanka Ringwald     uint8_t event[11];
547f4d3b82aSMilanka Ringwald     int pos = 0;
548f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
549f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
550f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
551f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
552f4d3b82aSMilanka Ringwald     pos += 2;
553f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
554f4d3b82aSMilanka Ringwald 
555f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
556f4d3b82aSMilanka Ringwald     pos += 3;
557f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
558f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
559f4d3b82aSMilanka Ringwald 
560f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
561f4d3b82aSMilanka Ringwald }
562f4d3b82aSMilanka Ringwald 
563af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
564af2241c2SMilanka Ringwald     if (value_len != 1) return;
565af2241c2SMilanka Ringwald 
566af2241c2SMilanka Ringwald     uint8_t event[11];
567af2241c2SMilanka Ringwald     int pos = 0;
568af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
569af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
570af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
571af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
572af2241c2SMilanka Ringwald     pos += 2;
573af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
574af2241c2SMilanka Ringwald     event[pos++] = value[0];
575af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
576af2241c2SMilanka Ringwald }
577f4d3b82aSMilanka Ringwald 
57883d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
579021192e1SMilanka Ringwald     UNUSED(packet_type);
580021192e1SMilanka Ringwald     UNUSED(channel);
58183d7ed1cSMilanka Ringwald 
582021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
583021192e1SMilanka Ringwald 
584021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
585021192e1SMilanka Ringwald     btstack_assert(client != NULL);
586021192e1SMilanka Ringwald 
587021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
588021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
589021192e1SMilanka Ringwald         return;
590021192e1SMilanka Ringwald     }
591021192e1SMilanka Ringwald 
59283d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
593021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
59483d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
595021192e1SMilanka Ringwald }
596021192e1SMilanka Ringwald 
59783d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
59883d7ed1cSMilanka Ringwald     UNUSED(packet_type);
59983d7ed1cSMilanka Ringwald     UNUSED(channel);
60083d7ed1cSMilanka Ringwald 
60183d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
60283d7ed1cSMilanka Ringwald 
60383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
60483d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
60583d7ed1cSMilanka Ringwald 
60683d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
60783d7ed1cSMilanka Ringwald         return;
60883d7ed1cSMilanka Ringwald     }
60983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
61083d7ed1cSMilanka Ringwald 
61183d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
61283d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
61383d7ed1cSMilanka Ringwald         return;
61483d7ed1cSMilanka Ringwald     }
61583d7ed1cSMilanka Ringwald 
61683d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
61783d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
61883d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
61983d7ed1cSMilanka Ringwald }
620cf26c8fbSMilanka Ringwald 
621cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
622cf26c8fbSMilanka Ringwald     uint8_t att_status;
6236bcfb631SMilanka Ringwald     gatt_client_service_t service;
6246bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
625cf26c8fbSMilanka Ringwald 
626cf26c8fbSMilanka Ringwald     switch (client->state){
627cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
628556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
629556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
630556456ccSMilanka Ringwald #endif
631cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
63219146789SMilanka Ringwald 
63319146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
634cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
635cf26c8fbSMilanka Ringwald             UNUSED(att_status);
636cf26c8fbSMilanka Ringwald             break;
637cf26c8fbSMilanka Ringwald 
638cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
639556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
640556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
641556456ccSMilanka Ringwald #endif
6426bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6436bcfb631SMilanka Ringwald 
6446bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6456bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
64619146789SMilanka Ringwald 
64719146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6486bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6496bcfb631SMilanka Ringwald 
6506bcfb631SMilanka Ringwald             UNUSED(att_status);
6516bcfb631SMilanka Ringwald             break;
6526bcfb631SMilanka Ringwald 
65328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
654556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
655556456ccSMilanka 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);
656556456ccSMilanka Ringwald #endif
65728da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
658556456ccSMilanka Ringwald 
65919146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
66019146789SMilanka 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);
661e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
662e7bd2dbeSMilanka Ringwald             break;
663e7bd2dbeSMilanka Ringwald 
664e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
665556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
666556456ccSMilanka 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);
667556456ccSMilanka Ringwald #endif
668e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
669e7bd2dbeSMilanka Ringwald 
6703322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6713322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6723322b222SMilanka Ringwald 
67319146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
674e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
675e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
676e7bd2dbeSMilanka Ringwald             break;
677e7bd2dbeSMilanka Ringwald 
67828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
679556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
68019146789SMilanka 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);
681556456ccSMilanka Ringwald #endif
68228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
683e7bd2dbeSMilanka Ringwald 
68419146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
68519146789SMilanka 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);
686e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
687e7bd2dbeSMilanka Ringwald             break;
688e7bd2dbeSMilanka Ringwald 
6893322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
690556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
69119146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
692556456ccSMilanka Ringwald #endif
6933322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
694e7bd2dbeSMilanka Ringwald 
6953322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
6963322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
69719146789SMilanka Ringwald 
69819146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6993322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
700e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
701e7bd2dbeSMilanka Ringwald             break;
702e7bd2dbeSMilanka Ringwald 
70328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
704556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
70583d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
706556456ccSMilanka Ringwald                 client->report_index,
70783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
70883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
709556456ccSMilanka Ringwald #endif
71028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
711af2241c2SMilanka Ringwald             client->handle = 0;
712556456ccSMilanka Ringwald 
713556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
714556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
715556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
716556456ccSMilanka Ringwald 
71719146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
71870093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
71970093cf5SMilanka Ringwald             UNUSED(att_status);
72070093cf5SMilanka Ringwald             break;
72170093cf5SMilanka Ringwald 
72228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
72328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
72470093cf5SMilanka Ringwald 
72519146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
726af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
727af2241c2SMilanka Ringwald             client->handle = 0;
72870093cf5SMilanka Ringwald             UNUSED(att_status);
72970093cf5SMilanka Ringwald             break;
73070093cf5SMilanka Ringwald 
731cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
732cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
733cd28e7b1SMilanka Ringwald             if (client->value > 0){
734cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
735cd28e7b1SMilanka Ringwald             } else {
736cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
737cd28e7b1SMilanka Ringwald             }
738cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
739cd28e7b1SMilanka Ringwald                 client->report_index,
740cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
741cd28e7b1SMilanka Ringwald #endif
742cd28e7b1SMilanka Ringwald 
743cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
744cd28e7b1SMilanka Ringwald 
745cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
746cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
747cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
748cd28e7b1SMilanka Ringwald 
749cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
750cd28e7b1SMilanka Ringwald 
751cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
752cd28e7b1SMilanka Ringwald 
753cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
754cd28e7b1SMilanka Ringwald                 switch(client->value){
755cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
756cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
757cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
758cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
759cd28e7b1SMilanka Ringwald                         break;
760cd28e7b1SMilanka Ringwald                     default:
761cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
762cd28e7b1SMilanka Ringwald                         break;
763cd28e7b1SMilanka Ringwald                 }
764cd28e7b1SMilanka Ringwald             } else {
765cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
766cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
767cd28e7b1SMilanka Ringwald                     break;
768cd28e7b1SMilanka Ringwald                 }
769cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
770cd28e7b1SMilanka Ringwald             }
771cd28e7b1SMilanka Ringwald             break;
772cd28e7b1SMilanka Ringwald 
773021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
774cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
775cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
776cd28e7b1SMilanka Ringwald                 client->report_index,
777cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
778cd28e7b1SMilanka Ringwald #endif
779021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
780021192e1SMilanka Ringwald 
781021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
782021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
783021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
784021192e1SMilanka Ringwald 
78519146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
786021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
787021192e1SMilanka Ringwald 
788cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
789cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
790cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
791cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
792cd28e7b1SMilanka Ringwald             } else {
793021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
794021192e1SMilanka Ringwald                     hids_run_for_client(client);
795021192e1SMilanka Ringwald                     break;
796021192e1SMilanka Ringwald                 }
797021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
798021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
799021192e1SMilanka Ringwald             }
800021192e1SMilanka Ringwald             break;
801021192e1SMilanka Ringwald 
80283d7ed1cSMilanka Ringwald 
8036d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
80483d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8056d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
80683d7ed1cSMilanka Ringwald                 client->report_index,
80783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
80883d7ed1cSMilanka Ringwald #endif
80983d7ed1cSMilanka Ringwald 
8106d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
81183d7ed1cSMilanka Ringwald 
8126d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8136d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
8146d6f7efcSMilanka Ringwald                 &handle_report_event, client->con_handle,
81583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
81683d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
81783d7ed1cSMilanka Ringwald             UNUSED(att_status);
81883d7ed1cSMilanka Ringwald             break;
81983d7ed1cSMilanka Ringwald 
82083d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
82183d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
822f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
823f4d3b82aSMilanka Ringwald                 client->report_index,
82483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
82583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82683d7ed1cSMilanka Ringwald #endif
82783d7ed1cSMilanka Ringwald 
82883d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
829f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
83083d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
83183d7ed1cSMilanka Ringwald                 &handle_report_event,
83283d7ed1cSMilanka Ringwald                 client->con_handle,
83383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
83483d7ed1cSMilanka Ringwald             UNUSED(att_status);
83583d7ed1cSMilanka Ringwald             break;
83683d7ed1cSMilanka Ringwald 
83783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
83883d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
83983d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
84083d7ed1cSMilanka Ringwald 
841f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
84283d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
84383d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
84483d7ed1cSMilanka Ringwald                 client->con_handle,
84583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
84683d7ed1cSMilanka Ringwald 
84783d7ed1cSMilanka Ringwald             break;
84883d7ed1cSMilanka Ringwald #endif
849af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
850af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
85183d7ed1cSMilanka Ringwald 
852f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
853f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
854f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
855f4d3b82aSMilanka Ringwald                 client->con_handle,
856af2241c2SMilanka Ringwald                 client->handle);
857f4d3b82aSMilanka Ringwald             break;
8586d6f7efcSMilanka Ringwald 
859fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8606d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
861fd39e93aSMilanka Ringwald             (void) gatt_client_request_can_write_without_response_event(&handle_gatt_client_event, client->con_handle);
8626d6f7efcSMilanka Ringwald             break;
8636d6f7efcSMilanka Ringwald 
8646bcfb631SMilanka Ringwald         default:
8656bcfb631SMilanka Ringwald             break;
8666bcfb631SMilanka Ringwald     }
8676bcfb631SMilanka Ringwald }
8686bcfb631SMilanka Ringwald 
869cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
870cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
871cf26c8fbSMilanka Ringwald     UNUSED(channel);
872cf26c8fbSMilanka Ringwald     UNUSED(size);
873cf26c8fbSMilanka Ringwald 
8746bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
875cf26c8fbSMilanka Ringwald     uint8_t att_status;
8766bcfb631SMilanka Ringwald     gatt_client_service_t service;
8776bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
87870093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
879ab116b1cSMilanka Ringwald 
880021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
881021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
882e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
8833322b222SMilanka Ringwald     uint8_t i;
8843322b222SMilanka Ringwald     uint8_t report_index;
885da142a6fSMilanka Ringwald 
886f4d3b82aSMilanka Ringwald     const uint8_t * value;
887f4d3b82aSMilanka Ringwald     uint16_t value_len;
888cf26c8fbSMilanka Ringwald 
889cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
890cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
891cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
892cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
893cf26c8fbSMilanka Ringwald 
894021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
895021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
8966bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
897021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
898021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
8996bcfb631SMilanka Ringwald                 client->num_instances++;
900021192e1SMilanka Ringwald 
901708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
902708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
903708c69d2SMilanka Ringwald #endif
904021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
905021192e1SMilanka Ringwald             }  else {
906021192e1SMilanka 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);
907da142a6fSMilanka Ringwald             }
9086bcfb631SMilanka Ringwald             break;
9096bcfb631SMilanka Ringwald 
9106bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9116bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
9126bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
9136bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
91419146789SMilanka Ringwald 
91519146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
91619146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
91719146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
91819146789SMilanka Ringwald                     break;
91919146789SMilanka Ringwald                 }
92019146789SMilanka Ringwald             }
92119146789SMilanka Ringwald 
92219146789SMilanka Ringwald             switch (characteristic.uuid16){
92319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
924af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
92519146789SMilanka Ringwald                     break;
92619146789SMilanka Ringwald 
92719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
92819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
92919146789SMilanka Ringwald                     break;
93019146789SMilanka Ringwald 
93119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
93219146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
93319146789SMilanka Ringwald                     break;
93419146789SMilanka Ringwald 
93519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
936f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
93719146789SMilanka Ringwald                     break;
93819146789SMilanka Ringwald 
93919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
94019146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
94119146789SMilanka Ringwald                     break;
94219146789SMilanka Ringwald 
94319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94419146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
94519146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
94619146789SMilanka Ringwald                     break;
94719146789SMilanka Ringwald 
94819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
949f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
95019146789SMilanka Ringwald                     break;
95119146789SMilanka Ringwald 
95219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
953f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
95419146789SMilanka Ringwald                     break;
95519146789SMilanka Ringwald 
95619146789SMilanka Ringwald                 default:
95719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
95819146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
95919146789SMilanka Ringwald #endif
96019146789SMilanka Ringwald                     return;
96119146789SMilanka Ringwald             }
96219146789SMilanka Ringwald 
96319146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
96419146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
96519146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
96619146789SMilanka Ringwald                 characteristic.start_handle,
96719146789SMilanka Ringwald                 characteristic.properties,
96819146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
96919146789SMilanka Ringwald                 client->service_index);
97019146789SMilanka Ringwald 
97119146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
97219146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
97319146789SMilanka Ringwald             }
97419146789SMilanka Ringwald             printf("\n");
97519146789SMilanka Ringwald #endif
976cf26c8fbSMilanka Ringwald             break;
977cf26c8fbSMilanka Ringwald 
9788cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
979e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
9808cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
981e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
982da142a6fSMilanka Ringwald 
983f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
984f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
9858cec2b74SMilanka Ringwald 
986556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9878cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
988f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
989556456ccSMilanka Ringwald #endif
990f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
991f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
992da142a6fSMilanka Ringwald                 if (!stored){
993da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
994da142a6fSMilanka Ringwald                     break;
995da142a6fSMilanka Ringwald                 }
996da142a6fSMilanka Ringwald             }
997e7bd2dbeSMilanka Ringwald             break;
998e7bd2dbeSMilanka Ringwald 
99970093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
100070093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
100170093cf5SMilanka Ringwald             btstack_assert(client != NULL);
100270093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
100370093cf5SMilanka Ringwald 
1004e7bd2dbeSMilanka Ringwald             switch (client->state) {
1005e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1006e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1007e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
100819146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10093322b222SMilanka Ringwald 
1010556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1011556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1012556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1013556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1014556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1015556456ccSMilanka Ringwald                                 client->service_index, report_index);
1016556456ccSMilanka Ringwald                         }
1017556456ccSMilanka Ringwald #endif
1018e7bd2dbeSMilanka Ringwald                     }
1019e7bd2dbeSMilanka Ringwald                     break;
102028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
102170093cf5SMilanka Ringwald                     // setup for descriptor value query
102270093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1023af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1024556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1025556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1026556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1027556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
102819146789SMilanka Ringwald #endif
1029556456ccSMilanka Ringwald                     }
1030556456ccSMilanka Ringwald 
103119146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1032556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
103383d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1034556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1035556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1036556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1037556456ccSMilanka Ringwald                     }
1038556456ccSMilanka Ringwald #endif
103970093cf5SMilanka Ringwald                     break;
1040556456ccSMilanka Ringwald 
1041e7bd2dbeSMilanka Ringwald                 default:
1042e7bd2dbeSMilanka Ringwald                     break;
1043e7bd2dbeSMilanka Ringwald             }
1044e7bd2dbeSMilanka Ringwald             break;
104570093cf5SMilanka Ringwald 
104683d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
104783d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
104883d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
104983d7ed1cSMilanka Ringwald 
1050f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1051f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1052f4d3b82aSMilanka Ringwald 
1053af2241c2SMilanka Ringwald 
1054f4d3b82aSMilanka Ringwald             switch (client->state){
1055f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1056f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
105783d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1058f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
105983d7ed1cSMilanka Ringwald                     break;
106083d7ed1cSMilanka Ringwald #endif
1061af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1062af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1063af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1064f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1065f4d3b82aSMilanka Ringwald                         break;
1066af2241c2SMilanka Ringwald                     }
1067af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1068af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1069af2241c2SMilanka Ringwald                         break;
1070af2241c2SMilanka Ringwald                     }
1071af2241c2SMilanka Ringwald                     break;
1072af2241c2SMilanka Ringwald                 }
1073f4d3b82aSMilanka Ringwald                 default:
1074f4d3b82aSMilanka Ringwald                     break;
1075f4d3b82aSMilanka Ringwald             }
1076f4d3b82aSMilanka Ringwald 
1077f4d3b82aSMilanka Ringwald             break;
107883d7ed1cSMilanka Ringwald 
107970093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
108070093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
108170093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1082e7bd2dbeSMilanka Ringwald 
1083e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1084e7bd2dbeSMilanka Ringwald                 break;
1085e7bd2dbeSMilanka Ringwald             }
10867e1e6e7dSMilanka Ringwald 
1087e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1088e7bd2dbeSMilanka Ringwald             switch (client->state) {
108928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1090e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
109119146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
10923322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
109319146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1094556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
109519146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
109619146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1097556456ccSMilanka Ringwald #endif
10983322b222SMilanka Ringwald                     }
1099e7bd2dbeSMilanka Ringwald                     break;
1100e7bd2dbeSMilanka Ringwald 
110128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
110219146789SMilanka Ringwald 
110319146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1104021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1105021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
110619146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
110783d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1108021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1109021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
111083d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
111119146789SMilanka Ringwald     #endif
111219146789SMilanka Ringwald                     }
1113e7bd2dbeSMilanka Ringwald                     break;
1114e7bd2dbeSMilanka Ringwald 
1115e7bd2dbeSMilanka Ringwald                 default:
1116e7bd2dbeSMilanka Ringwald                     break;
111770093cf5SMilanka Ringwald             }
111870093cf5SMilanka Ringwald             break;
111970093cf5SMilanka Ringwald 
1120fd39e93aSMilanka Ringwald         case GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE:
1121fd39e93aSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_can_write_without_response_get_handle(packet));
1122fd39e93aSMilanka Ringwald             btstack_assert(client != NULL);
1123fd39e93aSMilanka Ringwald 
1124fd39e93aSMilanka Ringwald             switch (client->state){
1125fd39e93aSMilanka Ringwald                 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
1126fd39e93aSMilanka Ringwald                     att_status = gatt_client_write_value_of_characteristic_without_response(
1127fd39e93aSMilanka Ringwald                         client->con_handle,
1128fd39e93aSMilanka Ringwald                         client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
1129fd39e93aSMilanka Ringwald 
1130fd39e93aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1131fd39e93aSMilanka Ringwald                     printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
1132fd39e93aSMilanka Ringwald #endif
1133fd39e93aSMilanka Ringwald 
1134fd39e93aSMilanka Ringwald                     if (att_status == ATT_ERROR_SUCCESS){
1135fd39e93aSMilanka Ringwald                         client->services[client->service_index].protocol_mode = client->required_protocol_mode;
1136fd39e93aSMilanka Ringwald                         if ((client->service_index + 1) < client->num_instances){
1137fd39e93aSMilanka Ringwald                             client->service_index++;
1138fd39e93aSMilanka Ringwald                             hids_run_for_client(client);
1139fd39e93aSMilanka Ringwald                             break;
1140fd39e93aSMilanka Ringwald                         }
1141fd39e93aSMilanka Ringwald                     }
1142fd39e93aSMilanka Ringwald 
1143fd39e93aSMilanka Ringwald                     // read UUIDS for external characteristics
1144fd39e93aSMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1145fd39e93aSMilanka Ringwald                         hids_run_for_client(client);
1146fd39e93aSMilanka Ringwald                         break;
1147fd39e93aSMilanka Ringwald                     }
1148fd39e93aSMilanka Ringwald 
1149fd39e93aSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1150fd39e93aSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
1151fd39e93aSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1152fd39e93aSMilanka Ringwald                         hids_run_for_client(client);
1153fd39e93aSMilanka Ringwald                         break;
1154fd39e93aSMilanka Ringwald                     }
1155fd39e93aSMilanka Ringwald 
1156fd39e93aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1157fd39e93aSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1158fd39e93aSMilanka Ringwald                     break;
1159fd39e93aSMilanka Ringwald 
1160fd39e93aSMilanka Ringwald                 case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
1161fd39e93aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1162fd39e93aSMilanka Ringwald                     printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
1163fd39e93aSMilanka Ringwald #endif
1164fd39e93aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1165fd39e93aSMilanka Ringwald                     (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
1166fd39e93aSMilanka Ringwald                     break;
1167fd39e93aSMilanka Ringwald 
1168fd39e93aSMilanka Ringwald                 default:
1169fd39e93aSMilanka Ringwald                     break;
1170fd39e93aSMilanka Ringwald             }
1171fd39e93aSMilanka Ringwald 
1172fd39e93aSMilanka Ringwald             break;
1173fd39e93aSMilanka Ringwald 
1174cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1175cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1176cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1177cf26c8fbSMilanka Ringwald 
1178cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1179cf26c8fbSMilanka Ringwald 
1180cf26c8fbSMilanka Ringwald             switch (client->state){
1181cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
11826bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11836bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11846bcfb631SMilanka Ringwald                         hids_finalize_client(client);
1185cf26c8fbSMilanka Ringwald                         break;
11866bcfb631SMilanka Ringwald                     }
11876bcfb631SMilanka Ringwald 
11886bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
11896bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
11906bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11916bcfb631SMilanka Ringwald                         break;
11926bcfb631SMilanka Ringwald                     }
11936bcfb631SMilanka Ringwald 
11946bcfb631SMilanka Ringwald                     client->service_index = 0;
119519146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
11966bcfb631SMilanka Ringwald                     break;
11976bcfb631SMilanka Ringwald 
11986bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
11996bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12006bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12016bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12026bcfb631SMilanka Ringwald                         break;
12036bcfb631SMilanka Ringwald                     }
12046bcfb631SMilanka Ringwald 
120570093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
120670093cf5SMilanka Ringwald                         // discover characteristics of next service
120770093cf5SMilanka Ringwald                         client->service_index++;
120870093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1209021192e1SMilanka Ringwald                         break;
1210021192e1SMilanka Ringwald                     }
1211021192e1SMilanka Ringwald 
1212fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1213fd39e93aSMilanka Ringwald 
1214021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1215021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1216fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1217fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1218021192e1SMilanka Ringwald                             }
12197e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12207e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12217e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
122270093cf5SMilanka Ringwald                                 break;
122370093cf5SMilanka Ringwald                             }
122470093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
122570093cf5SMilanka Ringwald                             hids_finalize_client(client);
1226fd39e93aSMilanka Ringwald                             return;
1227fd39e93aSMilanka Ringwald 
1228fd39e93aSMilanka Ringwald                         default:
1229fd39e93aSMilanka Ringwald                             // set boot mode
1230fd39e93aSMilanka Ringwald                             client->service_index = 0;
1231fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1232fd39e93aSMilanka Ringwald                             break;
1233fd39e93aSMilanka Ringwald                     }
12346bcfb631SMilanka Ringwald                     break;
12356bcfb631SMilanka Ringwald 
12363322b222SMilanka Ringwald 
123728da36a6SMilanka Ringwald                 // HID descriptor found
123828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1239e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1240e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1241e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1242e7bd2dbeSMilanka Ringwald                         break;
1243e7bd2dbeSMilanka Ringwald                     }
1244e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1245e7bd2dbeSMilanka Ringwald                     break;
1246e7bd2dbeSMilanka Ringwald 
124728da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1248e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12493322b222SMilanka Ringwald                     // go for next report map
12503322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1251e7bd2dbeSMilanka Ringwald                         break;
1252e7bd2dbeSMilanka Ringwald                     }
1253e7bd2dbeSMilanka Ringwald 
12543322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12553322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1256e7bd2dbeSMilanka Ringwald                         break;
1257e7bd2dbeSMilanka Ringwald                     }
1258e7bd2dbeSMilanka Ringwald 
1259e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1260e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12617e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1262e7bd2dbeSMilanka Ringwald                         break;
1263e7bd2dbeSMilanka Ringwald                     }
1264e7bd2dbeSMilanka Ringwald 
1265e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1266e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1267e7bd2dbeSMilanka Ringwald                     break;
1268e7bd2dbeSMilanka Ringwald 
126928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1270e7bd2dbeSMilanka Ringwald                     // go for next map report
12713322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1272e7bd2dbeSMilanka Ringwald                         break;
1273e7bd2dbeSMilanka Ringwald                     }
1274e7bd2dbeSMilanka Ringwald 
12753322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12763322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1277e7bd2dbeSMilanka Ringwald                     break;
1278e7bd2dbeSMilanka Ringwald 
12793322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1280e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1281e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12827e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1283e7bd2dbeSMilanka Ringwald                         break;
1284e7bd2dbeSMilanka Ringwald                     }
1285e7bd2dbeSMilanka Ringwald 
1286e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1287e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1288e7bd2dbeSMilanka Ringwald                     break;
1289e7bd2dbeSMilanka Ringwald 
129028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1291af2241c2SMilanka Ringwald                     if (client->handle != 0){
129228da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
129370093cf5SMilanka Ringwald                         break;
129470093cf5SMilanka Ringwald                     }
129570093cf5SMilanka Ringwald                     // go for next report
12967e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
129770093cf5SMilanka Ringwald                         break;
129870093cf5SMilanka Ringwald                     }
1299835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
130070093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
130170093cf5SMilanka Ringwald                     break;
130270093cf5SMilanka Ringwald 
130328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
130470093cf5SMilanka Ringwald                     // go for next report
13057e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
130670093cf5SMilanka Ringwald                         break;
130770093cf5SMilanka Ringwald                     }
1308021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1309021192e1SMilanka Ringwald                         break;
1310021192e1SMilanka Ringwald                     }
1311835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
131270093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
131370093cf5SMilanka Ringwald                     break;
131470093cf5SMilanka Ringwald 
1315021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1316021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13172901a9b7SMilanka Ringwald                         break;
13182901a9b7SMilanka Ringwald                     }
1319835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13206bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13216bcfb631SMilanka Ringwald                     break;
1322f4d3b82aSMilanka Ringwald 
1323cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1324cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1325cd28e7b1SMilanka Ringwald                         break;
1326cd28e7b1SMilanka Ringwald                     }
1327cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1328cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1329cd28e7b1SMilanka Ringwald                     break;
1330cd28e7b1SMilanka Ringwald 
1331835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1332835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1333835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1334835a13f1SMilanka Ringwald                     break;
1335835a13f1SMilanka Ringwald #endif
1336f4d3b82aSMilanka Ringwald 
1337af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
13386d6f7efcSMilanka Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1339f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1340f4d3b82aSMilanka Ringwald                     break;
1341f4d3b82aSMilanka Ringwald 
13426d6f7efcSMilanka Ringwald 
1343cf26c8fbSMilanka Ringwald                 default:
1344cf26c8fbSMilanka Ringwald                     break;
1345cf26c8fbSMilanka Ringwald             }
1346cf26c8fbSMilanka Ringwald             break;
1347cf26c8fbSMilanka Ringwald 
1348cf26c8fbSMilanka Ringwald         default:
1349cf26c8fbSMilanka Ringwald             break;
1350cf26c8fbSMilanka Ringwald     }
13516bcfb631SMilanka Ringwald 
13526bcfb631SMilanka Ringwald     if (client != NULL){
13536bcfb631SMilanka Ringwald         hids_run_for_client(client);
13546bcfb631SMilanka Ringwald     }
1355cf26c8fbSMilanka Ringwald }
1356cf26c8fbSMilanka Ringwald 
13576bcfb631SMilanka 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){
1358cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1359cf26c8fbSMilanka Ringwald 
1360cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1361cf26c8fbSMilanka Ringwald     if (client != NULL){
1362cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1363cf26c8fbSMilanka Ringwald     }
1364cf26c8fbSMilanka Ringwald 
1365cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1366cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1367cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1368cf26c8fbSMilanka Ringwald     }
1369cf26c8fbSMilanka Ringwald 
1370cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1371cf26c8fbSMilanka Ringwald     if (client == NULL) {
1372cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1373cf26c8fbSMilanka Ringwald     }
1374cf26c8fbSMilanka Ringwald 
13756bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1376cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1377cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1378cf26c8fbSMilanka Ringwald 
1379cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1380cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1381cf26c8fbSMilanka Ringwald }
1382cf26c8fbSMilanka Ringwald 
1383cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1384cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1385cf26c8fbSMilanka Ringwald     if (client == NULL){
1386cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1387cf26c8fbSMilanka Ringwald     }
1388cf26c8fbSMilanka Ringwald     // finalize connection
1389cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1390cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1391cf26c8fbSMilanka Ringwald }
1392cf26c8fbSMilanka Ringwald 
1393fd39e93aSMilanka Ringwald uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type, const uint8_t * report, uint8_t report_len){
13941624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
13951624214bSMilanka Ringwald     if (client == NULL){
13961624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
13971624214bSMilanka Ringwald     }
13981624214bSMilanka Ringwald 
13991624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14001624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14011624214bSMilanka Ringwald     }
14021624214bSMilanka Ringwald 
1403fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
140483d7ed1cSMilanka Ringwald 
14051624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14061624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14071624214bSMilanka Ringwald     }
14081624214bSMilanka Ringwald 
14091624214bSMilanka Ringwald     uint16_t mtu;
14101624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14111624214bSMilanka Ringwald 
14121624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14131624214bSMilanka Ringwald         return status;
14141624214bSMilanka Ringwald     }
14151624214bSMilanka Ringwald 
14161624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14171624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14181624214bSMilanka Ringwald     }
14191624214bSMilanka Ringwald 
14206d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1421021192e1SMilanka Ringwald     client->report_index = report_index;
14221624214bSMilanka Ringwald     client->report = report;
14231624214bSMilanka Ringwald     client->report_len = report_len;
14241624214bSMilanka Ringwald 
14251624214bSMilanka Ringwald     hids_run_for_client(client);
14261624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14271624214bSMilanka Ringwald }
14281624214bSMilanka Ringwald 
1429fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
143083d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
143183d7ed1cSMilanka Ringwald     if (client == NULL){
143283d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
143383d7ed1cSMilanka Ringwald     }
143483d7ed1cSMilanka Ringwald 
143583d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
143683d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
143783d7ed1cSMilanka Ringwald     }
143883d7ed1cSMilanka Ringwald 
1439fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
144083d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
144183d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
144283d7ed1cSMilanka Ringwald     }
144383d7ed1cSMilanka Ringwald 
144483d7ed1cSMilanka Ringwald     client->report_index = report_index;
144583d7ed1cSMilanka Ringwald 
144683d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
144783d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
144883d7ed1cSMilanka Ringwald #else
144983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
145083d7ed1cSMilanka Ringwald #endif
145183d7ed1cSMilanka Ringwald     hids_run_for_client(client);
145283d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
145383d7ed1cSMilanka Ringwald }
145483d7ed1cSMilanka Ringwald 
145583d7ed1cSMilanka Ringwald 
1456f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1457f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1458f4d3b82aSMilanka Ringwald     if (client == NULL){
1459f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1460f4d3b82aSMilanka Ringwald     }
1461f4d3b82aSMilanka Ringwald 
1462f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1463f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1464f4d3b82aSMilanka Ringwald     }
1465f4d3b82aSMilanka Ringwald 
1466f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1467f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1468f4d3b82aSMilanka Ringwald     }
1469f4d3b82aSMilanka Ringwald 
1470f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1471af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1472af2241c2SMilanka Ringwald 
1473af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1474af2241c2SMilanka Ringwald     hids_run_for_client(client);
1475af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1476af2241c2SMilanka Ringwald }
1477af2241c2SMilanka Ringwald 
1478af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1479af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1480af2241c2SMilanka Ringwald     if (client == NULL){
1481af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1482af2241c2SMilanka Ringwald     }
1483af2241c2SMilanka Ringwald 
1484af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1485af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1486af2241c2SMilanka Ringwald     }
1487af2241c2SMilanka Ringwald 
1488af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1489af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1490af2241c2SMilanka Ringwald     }
1491af2241c2SMilanka Ringwald 
1492af2241c2SMilanka Ringwald     client->service_index = service_index;
1493af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1494af2241c2SMilanka Ringwald 
1495af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1496f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1497f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1498f4d3b82aSMilanka Ringwald }
1499f4d3b82aSMilanka Ringwald 
1500*0cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15016d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15026d6f7efcSMilanka Ringwald     if (client == NULL){
15036d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15046d6f7efcSMilanka Ringwald     }
15056d6f7efcSMilanka Ringwald 
15066d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15076d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15086d6f7efcSMilanka Ringwald     }
15096d6f7efcSMilanka Ringwald 
15106d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15116d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15126d6f7efcSMilanka Ringwald     }
15136d6f7efcSMilanka Ringwald 
15146d6f7efcSMilanka Ringwald     client->service_index = service_index;
15156d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15166d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15176d6f7efcSMilanka Ringwald 
15186d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15196d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15206d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15216d6f7efcSMilanka Ringwald }
15226d6f7efcSMilanka Ringwald 
15236d6f7efcSMilanka Ringwald 
15246d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15256d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15266d6f7efcSMilanka Ringwald     if (client == NULL){
15276d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15286d6f7efcSMilanka Ringwald     }
15296d6f7efcSMilanka Ringwald 
15306d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15316d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15326d6f7efcSMilanka Ringwald     }
15336d6f7efcSMilanka Ringwald 
15346d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15356d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15366d6f7efcSMilanka Ringwald     }
15376d6f7efcSMilanka Ringwald 
15386d6f7efcSMilanka Ringwald     client->service_index = service_index;
15396d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15406d6f7efcSMilanka Ringwald     client->value = value;
15416d6f7efcSMilanka Ringwald 
15426d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15436d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15446d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15456d6f7efcSMilanka Ringwald }
15466d6f7efcSMilanka Ringwald 
15476d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15486d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15496d6f7efcSMilanka Ringwald }
15506d6f7efcSMilanka Ringwald 
15516d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
15526d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
15536d6f7efcSMilanka Ringwald }
15546d6f7efcSMilanka Ringwald 
1555*0cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1556cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1557cd28e7b1SMilanka Ringwald     if (client == NULL){
1558cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1559cd28e7b1SMilanka Ringwald     }
1560cd28e7b1SMilanka Ringwald 
1561cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1562cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1563cd28e7b1SMilanka Ringwald     }
1564cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1565cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1566cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1567cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1568cd28e7b1SMilanka Ringwald     }
1569cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1570cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1571cd28e7b1SMilanka Ringwald }
1572cd28e7b1SMilanka Ringwald 
1573*0cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1574cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1575cd28e7b1SMilanka Ringwald     if (client == NULL){
1576cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1577cd28e7b1SMilanka Ringwald     }
1578cd28e7b1SMilanka Ringwald 
1579cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1580cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1581cd28e7b1SMilanka Ringwald     }
1582cd28e7b1SMilanka Ringwald 
1583cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1584cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1585cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1586cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1587cd28e7b1SMilanka Ringwald     }
1588cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1589cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1590cd28e7b1SMilanka Ringwald }
15916d6f7efcSMilanka Ringwald 
1592021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1593021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1594021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1595021192e1SMilanka Ringwald }
1596cf26c8fbSMilanka Ringwald 
1597cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1598