xref: /btstack/src/ble/gatt-service/hids_client.c (revision e3bccb1cc2c858a67f07bbc70086237dec6c9396)
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
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH 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/core.h"
53fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
54cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
55fc975d0eSMilanka Ringwald #include "btstack_debug.h"
56fc975d0eSMilanka Ringwald #include "btstack_event.h"
57fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
58fc975d0eSMilanka Ringwald #include "gap.h"
59fc975d0eSMilanka Ringwald 
6070093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6470093cf5SMilanka Ringwald 
65da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
66da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
67da142a6fSMilanka Ringwald 
68da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
69da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
70da142a6fSMilanka Ringwald 
71cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
721fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context);
73cf26c8fbSMilanka Ringwald 
74556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
75556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
76556456ccSMilanka Ringwald     switch (uuid){
77556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
78556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
79556456ccSMilanka Ringwald 
80556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
81556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
82556456ccSMilanka Ringwald 
83556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
84556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
85556456ccSMilanka Ringwald 
86556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
87556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
88556456ccSMilanka Ringwald 
89556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
90556456ccSMilanka Ringwald             return "REPORT";
91556456ccSMilanka Ringwald 
92556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
93556456ccSMilanka Ringwald             return "REPORT_MAP";
94556456ccSMilanka Ringwald 
95556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
96556456ccSMilanka Ringwald             return "HID_INFORMATION";
97556456ccSMilanka Ringwald 
98556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
99556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
100556456ccSMilanka Ringwald         default:
101556456ccSMilanka Ringwald             return "UKNOWN";
102556456ccSMilanka Ringwald     }
103556456ccSMilanka Ringwald }
104556456ccSMilanka Ringwald #endif
105556456ccSMilanka Ringwald 
106da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
107da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
109da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
110da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
111da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
112da142a6fSMilanka Ringwald         return client;
113da142a6fSMilanka Ringwald     }
114da142a6fSMilanka Ringwald     return NULL;
115da142a6fSMilanka Ringwald }
116da142a6fSMilanka Ringwald 
117da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
118da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
120da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
121da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
122da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
123da142a6fSMilanka Ringwald         return client;
124da142a6fSMilanka Ringwald     }
125da142a6fSMilanka Ringwald     return NULL;
126da142a6fSMilanka Ringwald }
127da142a6fSMilanka Ringwald 
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald // START Descriptor Storage Util
130da142a6fSMilanka Ringwald 
131a391be9aSMatthias Ringwald static uint16_t hids_client_descriptors_len(hids_client_t * client){
132a391be9aSMatthias Ringwald     uint16_t descriptors_len = 0;
133a391be9aSMatthias Ringwald     uint8_t service_index;
134a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
135a391be9aSMatthias Ringwald         descriptors_len += client->services[service_index].hid_descriptor_len;
136a391be9aSMatthias Ringwald     }
137a391be9aSMatthias Ringwald     return descriptors_len;
138a391be9aSMatthias Ringwald }
139a391be9aSMatthias Ringwald 
140da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
141da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
142da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
143da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
144da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
145da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
146da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
147a391be9aSMatthias Ringwald         free_space -= hids_client_descriptors_len(client);
148da142a6fSMilanka Ringwald     }
149da142a6fSMilanka Ringwald     return free_space;
150da142a6fSMilanka Ringwald }
151da142a6fSMilanka Ringwald 
152da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
153a391be9aSMatthias Ringwald     // reserve remaining space for this connection
154a391be9aSMatthias Ringwald     uint16_t available_space = hids_client_descriptor_storage_get_available_space();
155da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
156a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_max_len = available_space;
157a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
158da142a6fSMilanka Ringwald }
159da142a6fSMilanka Ringwald 
160da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
161a391be9aSMatthias Ringwald     // store single hid descriptor byte
162da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
163da142a6fSMilanka Ringwald 
164da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
165da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
166da142a6fSMilanka Ringwald     return true;
167da142a6fSMilanka Ringwald }
168da142a6fSMilanka Ringwald 
169da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
170a391be9aSMatthias Ringwald     uint8_t service_index;
171da142a6fSMilanka Ringwald 
172a391be9aSMatthias Ringwald     // calculate descriptors len
173a391be9aSMatthias Ringwald     uint16_t descriptors_len = hids_client_descriptors_len(client);
174da142a6fSMilanka Ringwald 
175a391be9aSMatthias Ringwald     if (descriptors_len > 0){
176a391be9aSMatthias Ringwald         // move higher descriptors down
177a391be9aSMatthias Ringwald         uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
178da142a6fSMilanka Ringwald         memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
179da142a6fSMilanka Ringwald                 &hids_client_descriptor_storage[next_offset],
180da142a6fSMilanka Ringwald                 hids_client_descriptor_storage_len - next_offset);
181da142a6fSMilanka Ringwald 
182a391be9aSMatthias Ringwald         // fix descriptor offset of higher descriptors
183da142a6fSMilanka Ringwald         btstack_linked_list_iterator_t it;
184da142a6fSMilanka Ringwald         btstack_linked_list_iterator_init(&it, &clients);
185da142a6fSMilanka Ringwald         while (btstack_linked_list_iterator_has_next(&it)){
186da142a6fSMilanka Ringwald             hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
187a391be9aSMatthias Ringwald             if (conn == client) continue;
188a391be9aSMatthias Ringwald             for (service_index = 0; service_index < client->num_instances; service_index++){
189a391be9aSMatthias Ringwald                 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
190a391be9aSMatthias Ringwald                     conn->services[service_index].hid_descriptor_offset -= descriptors_len;
191da142a6fSMilanka Ringwald                 }
192da142a6fSMilanka Ringwald             }
193da142a6fSMilanka Ringwald         }
194da142a6fSMilanka Ringwald     }
195da142a6fSMilanka Ringwald 
196a391be9aSMatthias Ringwald     // clear descriptors
197a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
198a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_len = 0;
199a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_offset = 0;
200a391be9aSMatthias Ringwald     }
201a391be9aSMatthias Ringwald }
202a391be9aSMatthias Ringwald 
203da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
204da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
205021192e1SMilanka Ringwald     if (client == NULL){
206021192e1SMilanka Ringwald         return NULL;
207021192e1SMilanka Ringwald     }
208021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
209da142a6fSMilanka Ringwald         return NULL;
210da142a6fSMilanka Ringwald     }
211da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
212da142a6fSMilanka Ringwald }
213da142a6fSMilanka Ringwald 
214da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
215da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
216021192e1SMilanka Ringwald     if (client == NULL){
217021192e1SMilanka Ringwald         return 0;
218021192e1SMilanka Ringwald     }
219021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
220da142a6fSMilanka Ringwald         return 0;
221da142a6fSMilanka Ringwald     }
222da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
223da142a6fSMilanka Ringwald }
224da142a6fSMilanka Ringwald 
225da142a6fSMilanka Ringwald // END Descriptor Storage Util
226da142a6fSMilanka Ringwald 
227cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
228cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
229cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
230cf26c8fbSMilanka Ringwald     } else {
231cf26c8fbSMilanka Ringwald         hids_cid_counter++;
232cf26c8fbSMilanka Ringwald     }
233cf26c8fbSMilanka Ringwald     return hids_cid_counter;
234fc975d0eSMilanka Ringwald }
235fc975d0eSMilanka Ringwald 
236ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
237ab116b1cSMilanka Ringwald     uint8_t i;
238a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
239ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
240ab116b1cSMilanka Ringwald             return i;
241ab116b1cSMilanka Ringwald         }
242ab116b1cSMilanka Ringwald     }
243ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
244ab116b1cSMilanka Ringwald }
245ab116b1cSMilanka Ringwald 
24619146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
24719146789SMilanka Ringwald     uint8_t i;
24819146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24919146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
25019146789SMilanka Ringwald             return i;
25119146789SMilanka Ringwald         }
25219146789SMilanka Ringwald     }
25319146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
25419146789SMilanka Ringwald }
25519146789SMilanka Ringwald 
25619146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
25719146789SMilanka Ringwald     uint8_t i;
25819146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
25919146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
26019146789SMilanka Ringwald             return true;
26119146789SMilanka Ringwald         }
26219146789SMilanka Ringwald     }
26319146789SMilanka Ringwald     return false;
26419146789SMilanka Ringwald }
26519146789SMilanka Ringwald 
266fd39e93aSMilanka 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){
26783d7ed1cSMilanka Ringwald     uint8_t i;
268835a13f1SMilanka Ringwald 
26983d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
270fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
271fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
272fd39e93aSMilanka Ringwald 
273fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
27483d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
27583d7ed1cSMilanka Ringwald                 continue;
27683d7ed1cSMilanka Ringwald             }
277fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
27883d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
27983d7ed1cSMilanka Ringwald                 continue;
28083d7ed1cSMilanka Ringwald             }
281fd39e93aSMilanka Ringwald         }
282fd39e93aSMilanka Ringwald 
283fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
28483d7ed1cSMilanka Ringwald             return i;
28583d7ed1cSMilanka Ringwald         }
28683d7ed1cSMilanka Ringwald     }
28783d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
28883d7ed1cSMilanka Ringwald }
28919146789SMilanka Ringwald 
290021192e1SMilanka 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){
291ab116b1cSMilanka Ringwald 
29219146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
293ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2943322b222SMilanka Ringwald         return report_index;
295ab116b1cSMilanka Ringwald     }
29628da36a6SMilanka Ringwald     report_index = client->num_reports;
297ab116b1cSMilanka Ringwald 
29828da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
29928da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
30028da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
30128da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
302ab116b1cSMilanka Ringwald 
30328da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
30428da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
30528da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
306021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
30770093cf5SMilanka Ringwald 
308021192e1SMilanka 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);
309ab116b1cSMilanka Ringwald         client->num_reports++;
3103322b222SMilanka Ringwald         return report_index;
311ab116b1cSMilanka Ringwald     } else {
312ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3133322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
314ab116b1cSMilanka Ringwald     }
315ab116b1cSMilanka Ringwald }
316ab116b1cSMilanka Ringwald 
31719146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
318556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
319556456ccSMilanka Ringwald 
320556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
32119146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
322556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
32319146789SMilanka Ringwald 
324556456ccSMilanka Ringwald         client->num_external_reports++;
32519146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
326556456ccSMilanka Ringwald         return report_index;
327556456ccSMilanka Ringwald     } else {
328556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
329556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
33070093cf5SMilanka Ringwald     }
331556456ccSMilanka Ringwald }
332556456ccSMilanka Ringwald 
3337e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3347e1e6e7dSMilanka Ringwald     uint8_t i;
3353322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3363322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
337556456ccSMilanka Ringwald             return i;
3383322b222SMilanka Ringwald         }
3393322b222SMilanka Ringwald     }
340556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
341556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3423322b222SMilanka Ringwald }
3433322b222SMilanka Ringwald 
3443322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3453322b222SMilanka Ringwald     client->service_index++;
3463322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
347556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3483322b222SMilanka Ringwald         return true;
3493322b222SMilanka Ringwald     }
3503322b222SMilanka Ringwald     return false;
3513322b222SMilanka Ringwald }
3523322b222SMilanka Ringwald 
3533322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3543322b222SMilanka Ringwald     client->service_index = 0;
3553322b222SMilanka Ringwald 
3563322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3573322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3583322b222SMilanka Ringwald         return true;
3593322b222SMilanka Ringwald     }
3603322b222SMilanka Ringwald     return false;
3613322b222SMilanka Ringwald }
3623322b222SMilanka Ringwald 
3633322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
364021192e1SMilanka Ringwald     client->report_index++;
36519146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3663322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
367e7bd2dbeSMilanka Ringwald         return true;
368e7bd2dbeSMilanka Ringwald     }
369e7bd2dbeSMilanka Ringwald     return false;
370e7bd2dbeSMilanka Ringwald }
371e7bd2dbeSMilanka Ringwald 
3723322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
373021192e1SMilanka Ringwald     client->report_index = 0;
37419146789SMilanka Ringwald     if (client->num_external_reports > 0){
3753322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3767e1e6e7dSMilanka Ringwald         return true;
3777e1e6e7dSMilanka Ringwald     }
3787e1e6e7dSMilanka Ringwald     return false;
3797e1e6e7dSMilanka Ringwald }
3807e1e6e7dSMilanka Ringwald 
3817e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3827e1e6e7dSMilanka Ringwald     uint8_t i;
3837e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
384fd39e93aSMilanka Ringwald 
385021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
38670093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
387fd39e93aSMilanka Ringwald         if (!report.boot_report){
38870093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3897e1e6e7dSMilanka Ringwald                 index = i;
390021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3917e1e6e7dSMilanka Ringwald                 break;
39270093cf5SMilanka Ringwald             }
393fd39e93aSMilanka Ringwald         }
394fd39e93aSMilanka Ringwald     }
395021192e1SMilanka Ringwald     client->report_index = index;
3967e1e6e7dSMilanka Ringwald     return index;
3977e1e6e7dSMilanka Ringwald }
3987e1e6e7dSMilanka Ringwald 
3997e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
400021192e1SMilanka Ringwald     client->report_index++;
4017e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
40228da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
403e7bd2dbeSMilanka Ringwald         return true;
40470093cf5SMilanka Ringwald     }
4057e1e6e7dSMilanka Ringwald     return false;
4067e1e6e7dSMilanka Ringwald }
4077e1e6e7dSMilanka Ringwald 
4087e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
409021192e1SMilanka Ringwald     client->report_index = 0;
4107e1e6e7dSMilanka Ringwald 
4117e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
41228da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4137e1e6e7dSMilanka Ringwald         return true;
4147e1e6e7dSMilanka Ringwald     }
415e7bd2dbeSMilanka Ringwald     return false;
41670093cf5SMilanka Ringwald }
417ab116b1cSMilanka Ringwald 
418021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
419021192e1SMilanka Ringwald     uint8_t i;
420021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
421021192e1SMilanka Ringwald 
422021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
423021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
424fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
425fd39e93aSMilanka Ringwald 
426fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
427fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
428021192e1SMilanka Ringwald                 continue;
429021192e1SMilanka Ringwald             }
430fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
431fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
432fd39e93aSMilanka Ringwald                 continue;
433fd39e93aSMilanka Ringwald             }
434fd39e93aSMilanka Ringwald         }
435fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
436021192e1SMilanka Ringwald             index = i;
437021192e1SMilanka Ringwald         }
438021192e1SMilanka Ringwald     }
439021192e1SMilanka Ringwald     client->report_index = index;
440021192e1SMilanka Ringwald     return index;
441021192e1SMilanka Ringwald }
442021192e1SMilanka Ringwald 
443021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
444021192e1SMilanka Ringwald     client->report_index++;
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 
452021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
453cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
454cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
455cd28e7b1SMilanka Ringwald #endif
456021192e1SMilanka Ringwald     client->report_index = 0;
457021192e1SMilanka Ringwald 
458021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
459021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
460021192e1SMilanka Ringwald         return true;
461021192e1SMilanka Ringwald     }
462021192e1SMilanka Ringwald     return false;
463021192e1SMilanka Ringwald }
464021192e1SMilanka Ringwald 
465cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
466cd28e7b1SMilanka Ringwald     client->report_index++;
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 
474cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
475cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
476cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
477cd28e7b1SMilanka Ringwald #endif
478cd28e7b1SMilanka Ringwald     client->report_index = 0;
479cd28e7b1SMilanka Ringwald 
480cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
481cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
482cd28e7b1SMilanka Ringwald         return true;
483cd28e7b1SMilanka Ringwald     }
484cd28e7b1SMilanka Ringwald     return false;
485cd28e7b1SMilanka Ringwald }
486cd28e7b1SMilanka Ringwald 
487cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
488cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
489cf26c8fbSMilanka Ringwald     if (!client){
490cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
491cf26c8fbSMilanka Ringwald         return NULL;
492cf26c8fbSMilanka Ringwald     }
493cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
494cf26c8fbSMilanka Ringwald     client->cid = cid;
495cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
496fc975d0eSMilanka Ringwald 
497cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
498cf26c8fbSMilanka Ringwald     return client;
499fc975d0eSMilanka Ringwald }
500fc975d0eSMilanka Ringwald 
501cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
502021192e1SMilanka Ringwald     // stop listening
503021192e1SMilanka Ringwald     uint8_t i;
504021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
505021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
506021192e1SMilanka Ringwald     }
507021192e1SMilanka Ringwald 
508da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
509cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
510cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
511fc975d0eSMilanka Ringwald }
512cf26c8fbSMilanka Ringwald 
513cf26c8fbSMilanka Ringwald 
514cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5156bcfb631SMilanka Ringwald     uint8_t event[8];
516cf26c8fbSMilanka Ringwald     int pos = 0;
517cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
518cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
519cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
520cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
521cf26c8fbSMilanka Ringwald     pos += 2;
522cf26c8fbSMilanka Ringwald     event[pos++] = status;
523fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
524cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
525cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
526cf26c8fbSMilanka Ringwald }
527cf26c8fbSMilanka Ringwald 
528cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
529cd28e7b1SMilanka Ringwald     uint8_t event[6];
530cd28e7b1SMilanka Ringwald     int pos = 0;
531cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
532cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
533cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
534cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
535cd28e7b1SMilanka Ringwald     pos += 2;
536cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
537cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
538cd28e7b1SMilanka Ringwald }
539cd28e7b1SMilanka Ringwald 
540*e3bccb1cSMatthias Ringwald static uint16_t hids_client_setup_report_event(uint8_t subevent, hids_client_t *client, uint8_t report_index, uint8_t *buffer,
541*e3bccb1cSMatthias Ringwald                                uint16_t report_len) {
542021192e1SMilanka Ringwald     uint16_t pos = 0;
543021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
544021192e1SMilanka Ringwald     pos++;  // skip len
545*e3bccb1cSMatthias Ringwald     buffer[pos++] = subevent;
546021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
547021192e1SMilanka Ringwald     pos += 2;
548021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
549021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
550a2d3931bSMatthias Ringwald     little_endian_store_16(buffer, pos, report_len);
551021192e1SMilanka Ringwald     pos += 2;
552a2d3931bSMatthias Ringwald     buffer[1] = pos + report_len - 2;
553a2d3931bSMatthias Ringwald     return pos;
554a2d3931bSMatthias Ringwald }
55583d7ed1cSMilanka Ringwald 
556a2d3931bSMatthias Ringwald static void hids_client_setup_report_event_with_report_id(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
557*e3bccb1cSMatthias Ringwald     uint16_t pos = hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT, client, report_index, buffer,
558*e3bccb1cSMatthias Ringwald                                                   report_len + 1);
559a2d3931bSMatthias Ringwald     buffer[pos] = client->reports[report_index].report_id;
560021192e1SMilanka Ringwald }
561021192e1SMilanka Ringwald 
562f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
563f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
564f4d3b82aSMilanka Ringwald 
565f4d3b82aSMilanka Ringwald     uint8_t event[11];
566f4d3b82aSMilanka Ringwald     int pos = 0;
567f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
568f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
569f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
570f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
571f4d3b82aSMilanka Ringwald     pos += 2;
572f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
573f4d3b82aSMilanka Ringwald 
574f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
575f4d3b82aSMilanka Ringwald     pos += 3;
576f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
577f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
578f4d3b82aSMilanka Ringwald 
579f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
580f4d3b82aSMilanka Ringwald }
581f4d3b82aSMilanka Ringwald 
582af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
583af2241c2SMilanka Ringwald     if (value_len != 1) return;
584af2241c2SMilanka Ringwald 
585af2241c2SMilanka Ringwald     uint8_t event[11];
586af2241c2SMilanka Ringwald     int pos = 0;
587af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
588af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
589af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
590af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
591af2241c2SMilanka Ringwald     pos += 2;
592af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
593af2241c2SMilanka Ringwald     event[pos++] = value[0];
594af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
595af2241c2SMilanka Ringwald }
596f4d3b82aSMilanka Ringwald 
59783d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
598021192e1SMilanka Ringwald     UNUSED(packet_type);
599021192e1SMilanka Ringwald     UNUSED(channel);
60083d7ed1cSMilanka Ringwald 
601021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
602021192e1SMilanka Ringwald 
603021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
60417229983SMatthias Ringwald     if (client == NULL) return;
605021192e1SMilanka Ringwald 
606021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
607021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
608021192e1SMilanka Ringwald         return;
609021192e1SMilanka Ringwald     }
610021192e1SMilanka Ringwald 
61183d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
612a2d3931bSMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
613a2d3931bSMatthias Ringwald                                                   gatt_event_notification_get_value_length(packet));
61483d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
615021192e1SMilanka Ringwald }
616021192e1SMilanka Ringwald 
61783d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
61883d7ed1cSMilanka Ringwald     UNUSED(packet_type);
61983d7ed1cSMilanka Ringwald     UNUSED(channel);
62083d7ed1cSMilanka Ringwald 
62183d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
62283d7ed1cSMilanka Ringwald 
62383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
62417229983SMatthias Ringwald     if (client == NULL) return;
62583d7ed1cSMilanka Ringwald 
62683d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
62783d7ed1cSMilanka Ringwald         return;
62883d7ed1cSMilanka Ringwald     }
62983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
63083d7ed1cSMilanka Ringwald 
63183d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
63283d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
63383d7ed1cSMilanka Ringwald         return;
63483d7ed1cSMilanka Ringwald     }
63583d7ed1cSMilanka Ringwald 
63683d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
63784d4ab66SMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
63884d4ab66SMatthias Ringwald                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
63983d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
64083d7ed1cSMilanka Ringwald }
641cf26c8fbSMilanka Ringwald 
642cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
643cf26c8fbSMilanka Ringwald     uint8_t att_status;
6446bcfb631SMilanka Ringwald     gatt_client_service_t service;
6456bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
646cf26c8fbSMilanka Ringwald 
647cf26c8fbSMilanka Ringwald     switch (client->state){
648cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
649556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
650556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
651556456ccSMilanka Ringwald #endif
652cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
65319146789SMilanka Ringwald 
65419146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
655cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
656cf26c8fbSMilanka Ringwald             UNUSED(att_status);
657cf26c8fbSMilanka Ringwald             break;
658cf26c8fbSMilanka Ringwald 
659cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
660556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
661556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
662556456ccSMilanka Ringwald #endif
6636bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6646bcfb631SMilanka Ringwald 
6656bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6666bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
66719146789SMilanka Ringwald 
66819146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6696bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6706bcfb631SMilanka Ringwald 
6716bcfb631SMilanka Ringwald             UNUSED(att_status);
6726bcfb631SMilanka Ringwald             break;
6736bcfb631SMilanka Ringwald 
67428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
675556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
676556456ccSMilanka 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);
677556456ccSMilanka Ringwald #endif
67828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
679556456ccSMilanka Ringwald 
68019146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
68119146789SMilanka 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);
682e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
683e7bd2dbeSMilanka Ringwald             break;
684e7bd2dbeSMilanka Ringwald 
685e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
686556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
687556456ccSMilanka 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);
688556456ccSMilanka Ringwald #endif
689e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
690e7bd2dbeSMilanka Ringwald 
6913322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6923322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6933322b222SMilanka Ringwald 
69419146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
695e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
696e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
697e7bd2dbeSMilanka Ringwald             break;
698e7bd2dbeSMilanka Ringwald 
69928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
700556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
70119146789SMilanka 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);
702556456ccSMilanka Ringwald #endif
70328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
704e7bd2dbeSMilanka Ringwald 
70519146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
70619146789SMilanka 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);
707e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
708e7bd2dbeSMilanka Ringwald             break;
709e7bd2dbeSMilanka Ringwald 
7103322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
711556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
71219146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
713556456ccSMilanka Ringwald #endif
7143322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
715e7bd2dbeSMilanka Ringwald 
7163322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7173322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
71819146789SMilanka Ringwald 
71919146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7203322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
721e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
722e7bd2dbeSMilanka Ringwald             break;
723e7bd2dbeSMilanka Ringwald 
72428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
725556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
72683d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
727556456ccSMilanka Ringwald                 client->report_index,
72883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
72983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
730556456ccSMilanka Ringwald #endif
73128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
732af2241c2SMilanka Ringwald             client->handle = 0;
733556456ccSMilanka Ringwald 
734556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
735556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
736556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
737556456ccSMilanka Ringwald 
73819146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
73970093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
74070093cf5SMilanka Ringwald             UNUSED(att_status);
74170093cf5SMilanka Ringwald             break;
74270093cf5SMilanka Ringwald 
74328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
74428da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
74570093cf5SMilanka Ringwald 
74619146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
747af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
748af2241c2SMilanka Ringwald             client->handle = 0;
74970093cf5SMilanka Ringwald             UNUSED(att_status);
75070093cf5SMilanka Ringwald             break;
75170093cf5SMilanka Ringwald 
752cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
753cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
754cd28e7b1SMilanka Ringwald             if (client->value > 0){
755cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
756cd28e7b1SMilanka Ringwald             } else {
757cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
758cd28e7b1SMilanka Ringwald             }
759cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
760cd28e7b1SMilanka Ringwald                 client->report_index,
761cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
762cd28e7b1SMilanka Ringwald #endif
763cd28e7b1SMilanka Ringwald 
764cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
765cd28e7b1SMilanka Ringwald 
766cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
767cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
768cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
769cd28e7b1SMilanka Ringwald 
770cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
771cd28e7b1SMilanka Ringwald 
772cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
773cd28e7b1SMilanka Ringwald 
774cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
775cd28e7b1SMilanka Ringwald                 switch(client->value){
776cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
777cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
778cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
779cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
780cd28e7b1SMilanka Ringwald                         break;
781cd28e7b1SMilanka Ringwald                     default:
782cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
783cd28e7b1SMilanka Ringwald                         break;
784cd28e7b1SMilanka Ringwald                 }
785cd28e7b1SMilanka Ringwald             } else {
786cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
787cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
788cd28e7b1SMilanka Ringwald                     break;
789cd28e7b1SMilanka Ringwald                 }
790cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
791cd28e7b1SMilanka Ringwald             }
792cd28e7b1SMilanka Ringwald             break;
793cd28e7b1SMilanka Ringwald 
794021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
795cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
796cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
797cd28e7b1SMilanka Ringwald                 client->report_index,
798cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
799cd28e7b1SMilanka Ringwald #endif
800021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
801021192e1SMilanka Ringwald 
802021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
803021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
804021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
805021192e1SMilanka Ringwald 
80619146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
807021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
808021192e1SMilanka Ringwald 
809cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
810cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
811cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
812cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
813cd28e7b1SMilanka Ringwald             } else {
814021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
815021192e1SMilanka Ringwald                     hids_run_for_client(client);
816021192e1SMilanka Ringwald                     break;
817021192e1SMilanka Ringwald                 }
818021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
819021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
820021192e1SMilanka Ringwald             }
821021192e1SMilanka Ringwald             break;
822021192e1SMilanka Ringwald 
82383d7ed1cSMilanka Ringwald 
8246d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
82583d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8266d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
82783d7ed1cSMilanka Ringwald                 client->report_index,
82883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82983d7ed1cSMilanka Ringwald #endif
83083d7ed1cSMilanka Ringwald 
8316d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
83283d7ed1cSMilanka Ringwald 
8336d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8346d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
83584d4ab66SMatthias Ringwald                 &handle_gatt_client_event, client->con_handle,
83683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
83783d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
83883d7ed1cSMilanka Ringwald             UNUSED(att_status);
83983d7ed1cSMilanka Ringwald             break;
84083d7ed1cSMilanka Ringwald 
84183d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
84283d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
843f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
844f4d3b82aSMilanka Ringwald                 client->report_index,
84583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
84683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
84783d7ed1cSMilanka Ringwald #endif
84883d7ed1cSMilanka Ringwald 
84983d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
850f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
85183d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
85283d7ed1cSMilanka Ringwald                 &handle_report_event,
85383d7ed1cSMilanka Ringwald                 client->con_handle,
85483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
85583d7ed1cSMilanka Ringwald             UNUSED(att_status);
85683d7ed1cSMilanka Ringwald             break;
85783d7ed1cSMilanka Ringwald 
85883d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
85983d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
86083d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
86183d7ed1cSMilanka Ringwald 
862f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
86383d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
86483d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
86583d7ed1cSMilanka Ringwald                 client->con_handle,
86683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
86783d7ed1cSMilanka Ringwald 
86883d7ed1cSMilanka Ringwald             break;
86983d7ed1cSMilanka Ringwald #endif
870af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
871af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
87283d7ed1cSMilanka Ringwald 
873f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
874f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
875f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
876f4d3b82aSMilanka Ringwald                 client->con_handle,
877af2241c2SMilanka Ringwald                 client->handle);
878f4d3b82aSMilanka Ringwald             break;
8796d6f7efcSMilanka Ringwald 
880fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8816d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
8821fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
8831fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
8841fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
8851fa7278eSMatthias Ringwald             break;
8861fa7278eSMatthias Ringwald 
8871fa7278eSMatthias Ringwald         default:
8881fa7278eSMatthias Ringwald             break;
8891fa7278eSMatthias Ringwald     }
8901fa7278eSMatthias Ringwald }
8911fa7278eSMatthias Ringwald 
8921fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
8931fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
8941fa7278eSMatthias Ringwald     uint8_t att_status;
8951fa7278eSMatthias Ringwald     switch (client->state){
8961fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8971fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
8981fa7278eSMatthias Ringwald                 client->con_handle,
8991fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
9001fa7278eSMatthias Ringwald 
9011fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9021fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
9031fa7278eSMatthias Ringwald #endif
9041fa7278eSMatthias Ringwald 
9051fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
9061fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
9071fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
9081fa7278eSMatthias Ringwald                     client->service_index++;
9091fa7278eSMatthias Ringwald                     hids_run_for_client(client);
9101fa7278eSMatthias Ringwald                     break;
9111fa7278eSMatthias Ringwald                 }
9121fa7278eSMatthias Ringwald             }
9131fa7278eSMatthias Ringwald 
9141fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
9151fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
9161fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9171fa7278eSMatthias Ringwald                 break;
9181fa7278eSMatthias Ringwald             }
9191fa7278eSMatthias Ringwald 
9201fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
9211fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
9221fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
9231fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9241fa7278eSMatthias Ringwald                 break;
9251fa7278eSMatthias Ringwald             }
9261fa7278eSMatthias Ringwald 
9271fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9281fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9291fa7278eSMatthias Ringwald             break;
9301fa7278eSMatthias Ringwald 
9311fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9321fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9331fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9341fa7278eSMatthias Ringwald #endif
9351fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9361fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9376d6f7efcSMilanka Ringwald             break;
9386d6f7efcSMilanka Ringwald 
9396bcfb631SMilanka Ringwald         default:
9406bcfb631SMilanka Ringwald             break;
9416bcfb631SMilanka Ringwald     }
9426bcfb631SMilanka Ringwald }
9436bcfb631SMilanka Ringwald 
944cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
945cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
946cf26c8fbSMilanka Ringwald     UNUSED(channel);
947cf26c8fbSMilanka Ringwald     UNUSED(size);
948cf26c8fbSMilanka Ringwald 
9496bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
950cf26c8fbSMilanka Ringwald     uint8_t att_status;
9516bcfb631SMilanka Ringwald     gatt_client_service_t service;
9526bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
95370093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
954ab116b1cSMilanka Ringwald 
955021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
956021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
957e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9583322b222SMilanka Ringwald     uint8_t i;
9593322b222SMilanka Ringwald     uint8_t report_index;
960da142a6fSMilanka Ringwald 
961f4d3b82aSMilanka Ringwald     const uint8_t * value;
962f4d3b82aSMilanka Ringwald     uint16_t value_len;
963cf26c8fbSMilanka Ringwald 
964cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
965cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
966cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
96717229983SMatthias Ringwald             if (client == NULL) break;
968cf26c8fbSMilanka Ringwald 
969021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
970021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9716bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
972021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
973021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9746bcfb631SMilanka Ringwald                 client->num_instances++;
975021192e1SMilanka Ringwald 
976708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
977708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
978708c69d2SMilanka Ringwald #endif
979021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
980021192e1SMilanka Ringwald             }  else {
981021192e1SMilanka 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);
982da142a6fSMilanka Ringwald             }
9836bcfb631SMilanka Ringwald             break;
9846bcfb631SMilanka Ringwald 
9856bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9866bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
98717229983SMatthias Ringwald             if (client == NULL) break;
98817229983SMatthias Ringwald 
9896bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
99019146789SMilanka Ringwald 
99119146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
99219146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
99319146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
99419146789SMilanka Ringwald                     break;
99519146789SMilanka Ringwald                 }
99619146789SMilanka Ringwald             }
99719146789SMilanka Ringwald 
99819146789SMilanka Ringwald             switch (characteristic.uuid16){
99919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1000af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
100119146789SMilanka Ringwald                     break;
100219146789SMilanka Ringwald 
100319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
100419146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
100519146789SMilanka Ringwald                     break;
100619146789SMilanka Ringwald 
100719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
100819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
100919146789SMilanka Ringwald                     break;
101019146789SMilanka Ringwald 
101119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1012f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
101319146789SMilanka Ringwald                     break;
101419146789SMilanka Ringwald 
101519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
101619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
101719146789SMilanka Ringwald                     break;
101819146789SMilanka Ringwald 
101919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
102019146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
102119146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
102219146789SMilanka Ringwald                     break;
102319146789SMilanka Ringwald 
102419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1025f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
102619146789SMilanka Ringwald                     break;
102719146789SMilanka Ringwald 
102819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1029f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
103019146789SMilanka Ringwald                     break;
103119146789SMilanka Ringwald 
103219146789SMilanka Ringwald                 default:
103319146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103419146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
103519146789SMilanka Ringwald #endif
103619146789SMilanka Ringwald                     return;
103719146789SMilanka Ringwald             }
103819146789SMilanka Ringwald 
103919146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
104019146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
104119146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
104219146789SMilanka Ringwald                 characteristic.start_handle,
104319146789SMilanka Ringwald                 characteristic.properties,
104419146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
104519146789SMilanka Ringwald                 client->service_index);
104619146789SMilanka Ringwald 
104719146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
104819146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
104919146789SMilanka Ringwald             }
105019146789SMilanka Ringwald             printf("\n");
105119146789SMilanka Ringwald #endif
1052cf26c8fbSMilanka Ringwald             break;
1053cf26c8fbSMilanka Ringwald 
10548cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1055e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10568cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
105717229983SMatthias Ringwald             if (client == NULL) break;
1058da142a6fSMilanka Ringwald 
1059f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1060f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10618cec2b74SMilanka Ringwald 
1062556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10638cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1064f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1065556456ccSMilanka Ringwald #endif
1066f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1067f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1068da142a6fSMilanka Ringwald                 if (!stored){
1069da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1070da142a6fSMilanka Ringwald                     break;
1071da142a6fSMilanka Ringwald                 }
1072da142a6fSMilanka Ringwald             }
1073e7bd2dbeSMilanka Ringwald             break;
1074e7bd2dbeSMilanka Ringwald 
107570093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
107670093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
107717229983SMatthias Ringwald             if (client == NULL) break;
107817229983SMatthias Ringwald 
107970093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
108070093cf5SMilanka Ringwald 
1081e7bd2dbeSMilanka Ringwald             switch (client->state) {
1082e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1083e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1084e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
108519146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10863322b222SMilanka Ringwald 
1087556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1088556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1089556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1090556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1091556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1092556456ccSMilanka Ringwald                                 client->service_index, report_index);
1093556456ccSMilanka Ringwald                         }
1094556456ccSMilanka Ringwald #endif
1095e7bd2dbeSMilanka Ringwald                     }
1096e7bd2dbeSMilanka Ringwald                     break;
109728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
109870093cf5SMilanka Ringwald                     // setup for descriptor value query
109970093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1100af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1101556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1102556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1103556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1104556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
110519146789SMilanka Ringwald #endif
1106556456ccSMilanka Ringwald                     }
1107556456ccSMilanka Ringwald 
110819146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1109556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
111083d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1111556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1112556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1113556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1114556456ccSMilanka Ringwald                     }
1115556456ccSMilanka Ringwald #endif
111670093cf5SMilanka Ringwald                     break;
1117556456ccSMilanka Ringwald 
1118e7bd2dbeSMilanka Ringwald                 default:
1119e7bd2dbeSMilanka Ringwald                     break;
1120e7bd2dbeSMilanka Ringwald             }
1121e7bd2dbeSMilanka Ringwald             break;
112270093cf5SMilanka Ringwald 
112383d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
112483d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
112517229983SMatthias Ringwald             if (client == NULL) break;
112683d7ed1cSMilanka Ringwald 
1127f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1128f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1129f4d3b82aSMilanka Ringwald 
1130af2241c2SMilanka Ringwald 
1131f4d3b82aSMilanka Ringwald             switch (client->state){
1132f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1133f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
113483d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1135f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
113683d7ed1cSMilanka Ringwald                     break;
113783d7ed1cSMilanka Ringwald #endif
1138af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1139af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1140af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1141f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1142f4d3b82aSMilanka Ringwald                         break;
1143af2241c2SMilanka Ringwald                     }
1144af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1145af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1146af2241c2SMilanka Ringwald                         break;
1147af2241c2SMilanka Ringwald                     }
1148af2241c2SMilanka Ringwald                     break;
1149af2241c2SMilanka Ringwald                 }
1150f4d3b82aSMilanka Ringwald                 default:
1151f4d3b82aSMilanka Ringwald                     break;
1152f4d3b82aSMilanka Ringwald             }
1153f4d3b82aSMilanka Ringwald 
1154f4d3b82aSMilanka Ringwald             break;
115583d7ed1cSMilanka Ringwald 
115670093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
115770093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
115817229983SMatthias Ringwald             if (client == NULL) break;
1159e7bd2dbeSMilanka Ringwald 
1160e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1161e7bd2dbeSMilanka Ringwald                 break;
1162e7bd2dbeSMilanka Ringwald             }
11637e1e6e7dSMilanka Ringwald 
1164e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1165e7bd2dbeSMilanka Ringwald             switch (client->state) {
116628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1167e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
116819146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11693322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
117019146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1171556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
117219146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
117319146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1174556456ccSMilanka Ringwald #endif
11753322b222SMilanka Ringwald                     }
1176e7bd2dbeSMilanka Ringwald                     break;
1177e7bd2dbeSMilanka Ringwald 
117828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
117919146789SMilanka Ringwald 
118019146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1181021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1182021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
118319146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
118483d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1185021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1186021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
118783d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
118819146789SMilanka Ringwald     #endif
118919146789SMilanka Ringwald                     }
1190e7bd2dbeSMilanka Ringwald                     break;
1191e7bd2dbeSMilanka Ringwald 
1192e7bd2dbeSMilanka Ringwald                 default:
1193e7bd2dbeSMilanka Ringwald                     break;
119470093cf5SMilanka Ringwald             }
119570093cf5SMilanka Ringwald             break;
119670093cf5SMilanka Ringwald 
1197cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1198cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
119917229983SMatthias Ringwald             if (client == NULL) break;
1200cf26c8fbSMilanka Ringwald 
1201cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1202cf26c8fbSMilanka Ringwald 
1203cf26c8fbSMilanka Ringwald             switch (client->state){
1204cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
12056bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12066bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12076bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12085fa2c39aSMilanka Ringwald                         return;
12096bcfb631SMilanka Ringwald                     }
12106bcfb631SMilanka Ringwald 
12116bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12126bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12136bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12145fa2c39aSMilanka Ringwald                         return;
12156bcfb631SMilanka Ringwald                     }
12166bcfb631SMilanka Ringwald 
12176bcfb631SMilanka Ringwald                     client->service_index = 0;
121819146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12196bcfb631SMilanka Ringwald                     break;
12206bcfb631SMilanka Ringwald 
12216bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
12226bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12236bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12246bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12255fa2c39aSMilanka Ringwald                         return;
12266bcfb631SMilanka Ringwald                     }
12276bcfb631SMilanka Ringwald 
122870093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
122970093cf5SMilanka Ringwald                         // discover characteristics of next service
123070093cf5SMilanka Ringwald                         client->service_index++;
123170093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1232021192e1SMilanka Ringwald                         break;
1233021192e1SMilanka Ringwald                     }
1234021192e1SMilanka Ringwald 
1235fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1236fd39e93aSMilanka Ringwald 
1237021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1238021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1239fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1240fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1241021192e1SMilanka Ringwald                             }
12427e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12437e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12447e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
124570093cf5SMilanka Ringwald                                 break;
124670093cf5SMilanka Ringwald                             }
124770093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
124870093cf5SMilanka Ringwald                             hids_finalize_client(client);
1249fd39e93aSMilanka Ringwald                             return;
1250fd39e93aSMilanka Ringwald 
1251fd39e93aSMilanka Ringwald                         default:
1252fd39e93aSMilanka Ringwald                             // set boot mode
1253fd39e93aSMilanka Ringwald                             client->service_index = 0;
1254fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1255fd39e93aSMilanka Ringwald                             break;
1256fd39e93aSMilanka Ringwald                     }
12576bcfb631SMilanka Ringwald                     break;
12586bcfb631SMilanka Ringwald 
12593322b222SMilanka Ringwald 
126028da36a6SMilanka Ringwald                 // HID descriptor found
126128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1262e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1263e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1264e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12655fa2c39aSMilanka Ringwald                         return;
1266e7bd2dbeSMilanka Ringwald                     }
1267e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1268e7bd2dbeSMilanka Ringwald                     break;
1269e7bd2dbeSMilanka Ringwald 
127028da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1271e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12723322b222SMilanka Ringwald                     // go for next report map
12733322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1274e7bd2dbeSMilanka Ringwald                         break;
1275e7bd2dbeSMilanka Ringwald                     }
1276e7bd2dbeSMilanka Ringwald 
12773322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12783322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1279e7bd2dbeSMilanka Ringwald                         break;
1280e7bd2dbeSMilanka Ringwald                     }
1281e7bd2dbeSMilanka Ringwald 
1282e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1283e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12847e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1285e7bd2dbeSMilanka Ringwald                         break;
1286e7bd2dbeSMilanka Ringwald                     }
1287e7bd2dbeSMilanka Ringwald 
1288e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1289e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
12905fa2c39aSMilanka Ringwald                     return;
1291e7bd2dbeSMilanka Ringwald 
129228da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1293e7bd2dbeSMilanka Ringwald                     // go for next map report
12943322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1295e7bd2dbeSMilanka Ringwald                         break;
1296e7bd2dbeSMilanka Ringwald                     }
1297e7bd2dbeSMilanka Ringwald 
12983322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12993322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1300e7bd2dbeSMilanka Ringwald                     break;
1301e7bd2dbeSMilanka Ringwald 
13023322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1303e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1304e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13057e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1306e7bd2dbeSMilanka Ringwald                         break;
1307e7bd2dbeSMilanka Ringwald                     }
1308e7bd2dbeSMilanka Ringwald 
1309e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1310e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13115fa2c39aSMilanka Ringwald                     return;
1312e7bd2dbeSMilanka Ringwald 
131328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1314af2241c2SMilanka Ringwald                     if (client->handle != 0){
131528da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
131670093cf5SMilanka Ringwald                         break;
131770093cf5SMilanka Ringwald                     }
131870093cf5SMilanka Ringwald                     // go for next report
13197e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
132070093cf5SMilanka Ringwald                         break;
132170093cf5SMilanka Ringwald                     }
1322835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
132370093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
132470093cf5SMilanka Ringwald                     break;
132570093cf5SMilanka Ringwald 
132628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
132770093cf5SMilanka Ringwald                     // go for next report
13287e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
132970093cf5SMilanka Ringwald                         break;
133070093cf5SMilanka Ringwald                     }
1331021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1332021192e1SMilanka Ringwald                         break;
1333021192e1SMilanka Ringwald                     }
1334835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
133570093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
133670093cf5SMilanka Ringwald                     break;
133770093cf5SMilanka Ringwald 
1338021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1339021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13402901a9b7SMilanka Ringwald                         break;
13412901a9b7SMilanka Ringwald                     }
1342835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13436bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13446bcfb631SMilanka Ringwald                     break;
1345f4d3b82aSMilanka Ringwald 
1346cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1347cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1348cd28e7b1SMilanka Ringwald                         break;
1349cd28e7b1SMilanka Ringwald                     }
1350cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1351cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1352cd28e7b1SMilanka Ringwald                     break;
1353cd28e7b1SMilanka Ringwald 
1354835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1355835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1356835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1357835a13f1SMilanka Ringwald                     break;
1358835a13f1SMilanka Ringwald #endif
1359f4d3b82aSMilanka Ringwald 
1360af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1361f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1362f4d3b82aSMilanka Ringwald                     break;
1363f4d3b82aSMilanka Ringwald 
136484d4ab66SMatthias Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
136584d4ab66SMatthias Ringwald                     {
136684d4ab66SMatthias Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
136784d4ab66SMatthias Ringwald 
136884d4ab66SMatthias Ringwald                         // emit empty report to signal done
136984d4ab66SMatthias Ringwald                         uint8_t event[9];
1370*e3bccb1cSMatthias Ringwald                         hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1371*e3bccb1cSMatthias Ringwald                                                        client->report_index, event, 0);
137284d4ab66SMatthias Ringwald                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
137384d4ab66SMatthias Ringwald                     }
137484d4ab66SMatthias Ringwald                     break;
13756d6f7efcSMilanka Ringwald 
1376cf26c8fbSMilanka Ringwald                 default:
1377cf26c8fbSMilanka Ringwald                     break;
1378cf26c8fbSMilanka Ringwald             }
1379cf26c8fbSMilanka Ringwald             break;
1380cf26c8fbSMilanka Ringwald 
1381cf26c8fbSMilanka Ringwald         default:
1382cf26c8fbSMilanka Ringwald             break;
1383cf26c8fbSMilanka Ringwald     }
13846bcfb631SMilanka Ringwald 
13856bcfb631SMilanka Ringwald     if (client != NULL){
13866bcfb631SMilanka Ringwald         hids_run_for_client(client);
13876bcfb631SMilanka Ringwald     }
1388cf26c8fbSMilanka Ringwald }
1389cf26c8fbSMilanka Ringwald 
13906bcfb631SMilanka 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){
1391cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1392cf26c8fbSMilanka Ringwald 
1393cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1394cf26c8fbSMilanka Ringwald     if (client != NULL){
1395cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1396cf26c8fbSMilanka Ringwald     }
1397cf26c8fbSMilanka Ringwald 
1398cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1399cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1400cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1401cf26c8fbSMilanka Ringwald     }
1402cf26c8fbSMilanka Ringwald 
1403cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1404cf26c8fbSMilanka Ringwald     if (client == NULL) {
1405cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1406cf26c8fbSMilanka Ringwald     }
1407cf26c8fbSMilanka Ringwald 
14086bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1409cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1410cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1411cf26c8fbSMilanka Ringwald 
1412cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1413cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1414cf26c8fbSMilanka Ringwald }
1415cf26c8fbSMilanka Ringwald 
1416cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1417cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1418cf26c8fbSMilanka Ringwald     if (client == NULL){
1419cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1420cf26c8fbSMilanka Ringwald     }
1421cf26c8fbSMilanka Ringwald     // finalize connection
1422cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1423cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1424cf26c8fbSMilanka Ringwald }
1425cf26c8fbSMilanka Ringwald 
1426fd39e93aSMilanka 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){
14271624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14281624214bSMilanka Ringwald     if (client == NULL){
14291624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14301624214bSMilanka Ringwald     }
14311624214bSMilanka Ringwald 
14321624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14331624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14341624214bSMilanka Ringwald     }
14351624214bSMilanka Ringwald 
1436fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
143783d7ed1cSMilanka Ringwald 
14381624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14391624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14401624214bSMilanka Ringwald     }
14411624214bSMilanka Ringwald 
14421624214bSMilanka Ringwald     uint16_t mtu;
14431624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14441624214bSMilanka Ringwald 
14451624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14461624214bSMilanka Ringwald         return status;
14471624214bSMilanka Ringwald     }
14481624214bSMilanka Ringwald 
14491624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14501624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14511624214bSMilanka Ringwald     }
14521624214bSMilanka Ringwald 
14536d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1454021192e1SMilanka Ringwald     client->report_index = report_index;
14551624214bSMilanka Ringwald     client->report = report;
14561624214bSMilanka Ringwald     client->report_len = report_len;
14571624214bSMilanka Ringwald 
14581624214bSMilanka Ringwald     hids_run_for_client(client);
14591624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14601624214bSMilanka Ringwald }
14611624214bSMilanka Ringwald 
1462fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
146383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
146483d7ed1cSMilanka Ringwald     if (client == NULL){
146583d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
146683d7ed1cSMilanka Ringwald     }
146783d7ed1cSMilanka Ringwald 
146883d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
146983d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
147083d7ed1cSMilanka Ringwald     }
147183d7ed1cSMilanka Ringwald 
1472fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
147383d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
147483d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
147583d7ed1cSMilanka Ringwald     }
147683d7ed1cSMilanka Ringwald 
147783d7ed1cSMilanka Ringwald     client->report_index = report_index;
147883d7ed1cSMilanka Ringwald 
147983d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
148083d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
148183d7ed1cSMilanka Ringwald #else
148283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
148383d7ed1cSMilanka Ringwald #endif
148483d7ed1cSMilanka Ringwald     hids_run_for_client(client);
148583d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
148683d7ed1cSMilanka Ringwald }
148783d7ed1cSMilanka Ringwald 
148883d7ed1cSMilanka Ringwald 
1489f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1490f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1491f4d3b82aSMilanka Ringwald     if (client == NULL){
1492f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1493f4d3b82aSMilanka Ringwald     }
1494f4d3b82aSMilanka Ringwald 
1495f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1496f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1497f4d3b82aSMilanka Ringwald     }
1498f4d3b82aSMilanka Ringwald 
1499f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1500f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1501f4d3b82aSMilanka Ringwald     }
1502f4d3b82aSMilanka Ringwald 
1503f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1504af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1505af2241c2SMilanka Ringwald 
1506af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1507af2241c2SMilanka Ringwald     hids_run_for_client(client);
1508af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1509af2241c2SMilanka Ringwald }
1510af2241c2SMilanka Ringwald 
1511af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1512af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1513af2241c2SMilanka Ringwald     if (client == NULL){
1514af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1515af2241c2SMilanka Ringwald     }
1516af2241c2SMilanka Ringwald 
1517af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1518af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1519af2241c2SMilanka Ringwald     }
1520af2241c2SMilanka Ringwald 
1521af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1522af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1523af2241c2SMilanka Ringwald     }
1524af2241c2SMilanka Ringwald 
1525af2241c2SMilanka Ringwald     client->service_index = service_index;
1526af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1527af2241c2SMilanka Ringwald 
1528af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1529f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1530f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1531f4d3b82aSMilanka Ringwald }
1532f4d3b82aSMilanka Ringwald 
15330cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15346d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15356d6f7efcSMilanka Ringwald     if (client == NULL){
15366d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15376d6f7efcSMilanka Ringwald     }
15386d6f7efcSMilanka Ringwald 
15396d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15406d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15416d6f7efcSMilanka Ringwald     }
15426d6f7efcSMilanka Ringwald 
15436d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15446d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15456d6f7efcSMilanka Ringwald     }
15466d6f7efcSMilanka Ringwald 
15476d6f7efcSMilanka Ringwald     client->service_index = service_index;
15486d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15496d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15506d6f7efcSMilanka Ringwald 
15516d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15526d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15536d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15546d6f7efcSMilanka Ringwald }
15556d6f7efcSMilanka Ringwald 
15566d6f7efcSMilanka Ringwald 
15576d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15586d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15596d6f7efcSMilanka Ringwald     if (client == NULL){
15606d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15616d6f7efcSMilanka Ringwald     }
15626d6f7efcSMilanka Ringwald 
15636d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15646d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15656d6f7efcSMilanka Ringwald     }
15666d6f7efcSMilanka Ringwald 
15676d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15686d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15696d6f7efcSMilanka Ringwald     }
15706d6f7efcSMilanka Ringwald 
15716d6f7efcSMilanka Ringwald     client->service_index = service_index;
15726d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15736d6f7efcSMilanka Ringwald     client->value = value;
15746d6f7efcSMilanka Ringwald 
15756d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15766d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15776d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15786d6f7efcSMilanka Ringwald }
15796d6f7efcSMilanka Ringwald 
15806d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15816d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15826d6f7efcSMilanka Ringwald }
15836d6f7efcSMilanka Ringwald 
15846d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
15856d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
15866d6f7efcSMilanka Ringwald }
15876d6f7efcSMilanka Ringwald 
15880cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1589cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1590cd28e7b1SMilanka Ringwald     if (client == NULL){
1591cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1592cd28e7b1SMilanka Ringwald     }
1593cd28e7b1SMilanka Ringwald 
1594cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1595cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1596cd28e7b1SMilanka Ringwald     }
1597cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1598cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1599cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1600cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1601cd28e7b1SMilanka Ringwald     }
1602cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1603cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1604cd28e7b1SMilanka Ringwald }
1605cd28e7b1SMilanka Ringwald 
16060cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1607cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1608cd28e7b1SMilanka Ringwald     if (client == NULL){
1609cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1610cd28e7b1SMilanka Ringwald     }
1611cd28e7b1SMilanka Ringwald 
1612cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1613cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1614cd28e7b1SMilanka Ringwald     }
1615cd28e7b1SMilanka Ringwald 
1616cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1617cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1618cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1619cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1620cd28e7b1SMilanka Ringwald     }
1621cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1622cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1623cd28e7b1SMilanka Ringwald }
16246d6f7efcSMilanka Ringwald 
1625021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1626021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1627021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1628021192e1SMilanka Ringwald }
1629cf26c8fbSMilanka Ringwald 
1630cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1631