xref: /btstack/src/ble/gatt-service/hids_client.c (revision d41c2d2832fa603036e0715bf8fda2e5f58bd44e)
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"
59b60c1680SMatthias Ringwald #include "ble/gatt_service_client.h"
60fc975d0eSMilanka Ringwald 
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6570093cf5SMilanka Ringwald 
66e5451bcdSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
67e5451bcdSMatthias Ringwald 
68da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
69da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
70da142a6fSMilanka Ringwald 
71da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
72da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
73da142a6fSMilanka Ringwald 
74cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
751fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context);
76cf26c8fbSMilanka Ringwald 
77556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
78556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
79556456ccSMilanka Ringwald     switch (uuid){
80556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
81556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
82556456ccSMilanka Ringwald 
83556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
84556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
85556456ccSMilanka Ringwald 
86556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
87556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
88556456ccSMilanka Ringwald 
89556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
90556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
91556456ccSMilanka Ringwald 
92556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
93556456ccSMilanka Ringwald             return "REPORT";
94556456ccSMilanka Ringwald 
95556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
96556456ccSMilanka Ringwald             return "REPORT_MAP";
97556456ccSMilanka Ringwald 
98556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
99556456ccSMilanka Ringwald             return "HID_INFORMATION";
100556456ccSMilanka Ringwald 
101556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
102556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
103556456ccSMilanka Ringwald         default:
104556456ccSMilanka Ringwald             return "UKNOWN";
105556456ccSMilanka Ringwald     }
106556456ccSMilanka Ringwald }
107556456ccSMilanka Ringwald #endif
108556456ccSMilanka Ringwald 
109da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
110da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
111da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
112da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
113da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
114da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
115da142a6fSMilanka Ringwald         return client;
116da142a6fSMilanka Ringwald     }
117da142a6fSMilanka Ringwald     return NULL;
118da142a6fSMilanka Ringwald }
119da142a6fSMilanka Ringwald 
120da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
121da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
122da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
123da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
124da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
125da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
126da142a6fSMilanka Ringwald         return client;
127da142a6fSMilanka Ringwald     }
128da142a6fSMilanka Ringwald     return NULL;
129da142a6fSMilanka Ringwald }
130da142a6fSMilanka Ringwald 
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald // START Descriptor Storage Util
133da142a6fSMilanka Ringwald 
134a391be9aSMatthias Ringwald static uint16_t hids_client_descriptors_len(hids_client_t * client){
135a391be9aSMatthias Ringwald     uint16_t descriptors_len = 0;
136a391be9aSMatthias Ringwald     uint8_t service_index;
137a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
138a391be9aSMatthias Ringwald         descriptors_len += client->services[service_index].hid_descriptor_len;
139a391be9aSMatthias Ringwald     }
140a391be9aSMatthias Ringwald     return descriptors_len;
141a391be9aSMatthias Ringwald }
142a391be9aSMatthias Ringwald 
143da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
144da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
145da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
146da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
147da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
148da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
149da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
150a391be9aSMatthias Ringwald         free_space -= hids_client_descriptors_len(client);
151da142a6fSMilanka Ringwald     }
152da142a6fSMilanka Ringwald     return free_space;
153da142a6fSMilanka Ringwald }
154da142a6fSMilanka Ringwald 
155da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
156a391be9aSMatthias Ringwald     // reserve remaining space for this connection
157a391be9aSMatthias Ringwald     uint16_t available_space = hids_client_descriptor_storage_get_available_space();
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
159a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_max_len = available_space;
160a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
161da142a6fSMilanka Ringwald }
162da142a6fSMilanka Ringwald 
163da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
164a391be9aSMatthias Ringwald     // store single hid descriptor byte
165da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
166da142a6fSMilanka Ringwald 
167da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
168da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
169da142a6fSMilanka Ringwald     return true;
170da142a6fSMilanka Ringwald }
171da142a6fSMilanka Ringwald 
172da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
173a391be9aSMatthias Ringwald     uint8_t service_index;
174da142a6fSMilanka Ringwald 
175a391be9aSMatthias Ringwald     // calculate descriptors len
176a391be9aSMatthias Ringwald     uint16_t descriptors_len = hids_client_descriptors_len(client);
177da142a6fSMilanka Ringwald 
178a391be9aSMatthias Ringwald     if (descriptors_len > 0){
179a391be9aSMatthias Ringwald         // move higher descriptors down
180a391be9aSMatthias Ringwald         uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
181da142a6fSMilanka Ringwald         memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
182da142a6fSMilanka Ringwald                 &hids_client_descriptor_storage[next_offset],
183da142a6fSMilanka Ringwald                 hids_client_descriptor_storage_len - next_offset);
184da142a6fSMilanka Ringwald 
185a391be9aSMatthias Ringwald         // fix descriptor offset of higher descriptors
186da142a6fSMilanka Ringwald         btstack_linked_list_iterator_t it;
187da142a6fSMilanka Ringwald         btstack_linked_list_iterator_init(&it, &clients);
188da142a6fSMilanka Ringwald         while (btstack_linked_list_iterator_has_next(&it)){
189da142a6fSMilanka Ringwald             hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
190a391be9aSMatthias Ringwald             if (conn == client) continue;
191a391be9aSMatthias Ringwald             for (service_index = 0; service_index < client->num_instances; service_index++){
192a391be9aSMatthias Ringwald                 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
193a391be9aSMatthias Ringwald                     conn->services[service_index].hid_descriptor_offset -= descriptors_len;
194da142a6fSMilanka Ringwald                 }
195da142a6fSMilanka Ringwald             }
196da142a6fSMilanka Ringwald         }
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald 
199a391be9aSMatthias Ringwald     // clear descriptors
200a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
201a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_len = 0;
202a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_offset = 0;
203a391be9aSMatthias Ringwald     }
204a391be9aSMatthias Ringwald }
205a391be9aSMatthias Ringwald 
206da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
207da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
208021192e1SMilanka Ringwald     if (client == NULL){
209021192e1SMilanka Ringwald         return NULL;
210021192e1SMilanka Ringwald     }
211021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
212da142a6fSMilanka Ringwald         return NULL;
213da142a6fSMilanka Ringwald     }
214da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
215da142a6fSMilanka Ringwald }
216da142a6fSMilanka Ringwald 
217da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
218da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
219021192e1SMilanka Ringwald     if (client == NULL){
220021192e1SMilanka Ringwald         return 0;
221021192e1SMilanka Ringwald     }
222021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
223da142a6fSMilanka Ringwald         return 0;
224da142a6fSMilanka Ringwald     }
225da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
226da142a6fSMilanka Ringwald }
227da142a6fSMilanka Ringwald 
228da142a6fSMilanka Ringwald // END Descriptor Storage Util
229da142a6fSMilanka Ringwald 
230cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
231cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
232cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
233cf26c8fbSMilanka Ringwald     } else {
234cf26c8fbSMilanka Ringwald         hids_cid_counter++;
235cf26c8fbSMilanka Ringwald     }
236cf26c8fbSMilanka Ringwald     return hids_cid_counter;
237fc975d0eSMilanka Ringwald }
238fc975d0eSMilanka Ringwald 
239ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
240ab116b1cSMilanka Ringwald     uint8_t i;
241a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
242ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
243ab116b1cSMilanka Ringwald             return i;
244ab116b1cSMilanka Ringwald         }
245ab116b1cSMilanka Ringwald     }
246ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
247ab116b1cSMilanka Ringwald }
248ab116b1cSMilanka Ringwald 
24919146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
25019146789SMilanka Ringwald     uint8_t i;
25119146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
25219146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
25319146789SMilanka Ringwald             return i;
25419146789SMilanka Ringwald         }
25519146789SMilanka Ringwald     }
25619146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
25719146789SMilanka Ringwald }
25819146789SMilanka Ringwald 
25919146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
26019146789SMilanka Ringwald     uint8_t i;
26119146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
26219146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
26319146789SMilanka Ringwald             return true;
26419146789SMilanka Ringwald         }
26519146789SMilanka Ringwald     }
26619146789SMilanka Ringwald     return false;
26719146789SMilanka Ringwald }
26819146789SMilanka Ringwald 
269fd39e93aSMilanka 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){
27083d7ed1cSMilanka Ringwald     uint8_t i;
271835a13f1SMilanka Ringwald 
27283d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
273fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
274fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
275fd39e93aSMilanka Ringwald 
276fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
27783d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
27883d7ed1cSMilanka Ringwald                 continue;
27983d7ed1cSMilanka Ringwald             }
280fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
28183d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
28283d7ed1cSMilanka Ringwald                 continue;
28383d7ed1cSMilanka Ringwald             }
284fd39e93aSMilanka Ringwald         }
285fd39e93aSMilanka Ringwald 
286fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
28783d7ed1cSMilanka Ringwald             return i;
28883d7ed1cSMilanka Ringwald         }
28983d7ed1cSMilanka Ringwald     }
29083d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
29183d7ed1cSMilanka Ringwald }
29219146789SMilanka Ringwald 
293021192e1SMilanka 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){
294ab116b1cSMilanka Ringwald 
29519146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
296ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2973322b222SMilanka Ringwald         return report_index;
298ab116b1cSMilanka Ringwald     }
29928da36a6SMilanka Ringwald     report_index = client->num_reports;
300ab116b1cSMilanka Ringwald 
30128da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
30228da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
30328da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
30428da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
305ab116b1cSMilanka Ringwald 
30628da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
30728da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
30828da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
309021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
31070093cf5SMilanka Ringwald 
311021192e1SMilanka 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);
312ab116b1cSMilanka Ringwald         client->num_reports++;
3133322b222SMilanka Ringwald         return report_index;
314ab116b1cSMilanka Ringwald     } else {
315ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3163322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
317ab116b1cSMilanka Ringwald     }
318ab116b1cSMilanka Ringwald }
319ab116b1cSMilanka Ringwald 
32019146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
321556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
322556456ccSMilanka Ringwald 
323556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
32419146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
325556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
32619146789SMilanka Ringwald 
327556456ccSMilanka Ringwald         client->num_external_reports++;
32819146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
329556456ccSMilanka Ringwald         return report_index;
330556456ccSMilanka Ringwald     } else {
331556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
332556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
33370093cf5SMilanka Ringwald     }
334556456ccSMilanka Ringwald }
335556456ccSMilanka Ringwald 
3367e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3377e1e6e7dSMilanka Ringwald     uint8_t i;
3383322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3393322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
340556456ccSMilanka Ringwald             return i;
3413322b222SMilanka Ringwald         }
3423322b222SMilanka Ringwald     }
343556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
344556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3453322b222SMilanka Ringwald }
3463322b222SMilanka Ringwald 
3473322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3483322b222SMilanka Ringwald     client->service_index++;
3493322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
350556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3513322b222SMilanka Ringwald         return true;
3523322b222SMilanka Ringwald     }
3533322b222SMilanka Ringwald     return false;
3543322b222SMilanka Ringwald }
3553322b222SMilanka Ringwald 
3563322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3573322b222SMilanka Ringwald     client->service_index = 0;
3583322b222SMilanka Ringwald 
3593322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3603322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3613322b222SMilanka Ringwald         return true;
3623322b222SMilanka Ringwald     }
3633322b222SMilanka Ringwald     return false;
3643322b222SMilanka Ringwald }
3653322b222SMilanka Ringwald 
3663322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
367021192e1SMilanka Ringwald     client->report_index++;
36819146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3693322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
370e7bd2dbeSMilanka Ringwald         return true;
371e7bd2dbeSMilanka Ringwald     }
372e7bd2dbeSMilanka Ringwald     return false;
373e7bd2dbeSMilanka Ringwald }
374e7bd2dbeSMilanka Ringwald 
3753322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
376021192e1SMilanka Ringwald     client->report_index = 0;
37719146789SMilanka Ringwald     if (client->num_external_reports > 0){
3783322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3797e1e6e7dSMilanka Ringwald         return true;
3807e1e6e7dSMilanka Ringwald     }
3817e1e6e7dSMilanka Ringwald     return false;
3827e1e6e7dSMilanka Ringwald }
3837e1e6e7dSMilanka Ringwald 
3847e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3857e1e6e7dSMilanka Ringwald     uint8_t i;
3867e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
387fd39e93aSMilanka Ringwald 
388021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
38970093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
390fd39e93aSMilanka Ringwald         if (!report.boot_report){
39170093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3927e1e6e7dSMilanka Ringwald                 index = i;
393021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3947e1e6e7dSMilanka Ringwald                 break;
39570093cf5SMilanka Ringwald             }
396fd39e93aSMilanka Ringwald         }
397fd39e93aSMilanka Ringwald     }
398021192e1SMilanka Ringwald     client->report_index = index;
3997e1e6e7dSMilanka Ringwald     return index;
4007e1e6e7dSMilanka Ringwald }
4017e1e6e7dSMilanka Ringwald 
4027e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
403021192e1SMilanka Ringwald     client->report_index++;
4047e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
40528da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
406e7bd2dbeSMilanka Ringwald         return true;
40770093cf5SMilanka Ringwald     }
4087e1e6e7dSMilanka Ringwald     return false;
4097e1e6e7dSMilanka Ringwald }
4107e1e6e7dSMilanka Ringwald 
4117e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
412021192e1SMilanka Ringwald     client->report_index = 0;
4137e1e6e7dSMilanka Ringwald 
4147e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
41528da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4167e1e6e7dSMilanka Ringwald         return true;
4177e1e6e7dSMilanka Ringwald     }
418e7bd2dbeSMilanka Ringwald     return false;
41970093cf5SMilanka Ringwald }
420ab116b1cSMilanka Ringwald 
421021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
422021192e1SMilanka Ringwald     uint8_t i;
423021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
424021192e1SMilanka Ringwald 
425021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
426021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
427fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
428fd39e93aSMilanka Ringwald 
429fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
430fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
431021192e1SMilanka Ringwald                 continue;
432021192e1SMilanka Ringwald             }
433fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
434fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
435fd39e93aSMilanka Ringwald                 continue;
436fd39e93aSMilanka Ringwald             }
437fd39e93aSMilanka Ringwald         }
438fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
439021192e1SMilanka Ringwald             index = i;
440021192e1SMilanka Ringwald         }
441021192e1SMilanka Ringwald     }
442021192e1SMilanka Ringwald     client->report_index = index;
443021192e1SMilanka Ringwald     return index;
444021192e1SMilanka Ringwald }
445021192e1SMilanka Ringwald 
446021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
447021192e1SMilanka Ringwald     client->report_index++;
448021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
449021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
450021192e1SMilanka Ringwald         return true;
451021192e1SMilanka Ringwald     }
452021192e1SMilanka Ringwald     return false;
453021192e1SMilanka Ringwald }
454021192e1SMilanka Ringwald 
455021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
456cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
457cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
458cd28e7b1SMilanka Ringwald #endif
459021192e1SMilanka Ringwald     client->report_index = 0;
460021192e1SMilanka Ringwald 
461021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
462021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
463021192e1SMilanka Ringwald         return true;
464021192e1SMilanka Ringwald     }
465021192e1SMilanka Ringwald     return false;
466021192e1SMilanka Ringwald }
467021192e1SMilanka Ringwald 
468cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
469cd28e7b1SMilanka Ringwald     client->report_index++;
470cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
471cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
472cd28e7b1SMilanka Ringwald         return true;
473cd28e7b1SMilanka Ringwald     }
474cd28e7b1SMilanka Ringwald     return false;
475cd28e7b1SMilanka Ringwald }
476cd28e7b1SMilanka Ringwald 
477cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
478cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
479cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
480cd28e7b1SMilanka Ringwald #endif
481cd28e7b1SMilanka Ringwald     client->report_index = 0;
482cd28e7b1SMilanka Ringwald 
483cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
484cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
485cd28e7b1SMilanka Ringwald         return true;
486cd28e7b1SMilanka Ringwald     }
487cd28e7b1SMilanka Ringwald     return false;
488cd28e7b1SMilanka Ringwald }
489cd28e7b1SMilanka Ringwald 
490cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
491cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
492cf26c8fbSMilanka Ringwald     if (!client){
493cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
494cf26c8fbSMilanka Ringwald         return NULL;
495cf26c8fbSMilanka Ringwald     }
496cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
497cf26c8fbSMilanka Ringwald     client->cid = cid;
498cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
499fc975d0eSMilanka Ringwald 
500cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
501cf26c8fbSMilanka Ringwald     return client;
502fc975d0eSMilanka Ringwald }
503fc975d0eSMilanka Ringwald 
504cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
505021192e1SMilanka Ringwald     // stop listening
506021192e1SMilanka Ringwald     uint8_t i;
507021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
508021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
509021192e1SMilanka Ringwald     }
510021192e1SMilanka Ringwald 
511da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
512cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
513cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
514fc975d0eSMilanka Ringwald }
515cf26c8fbSMilanka Ringwald 
516cf26c8fbSMilanka Ringwald 
517cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5186bcfb631SMilanka Ringwald     uint8_t event[8];
519cf26c8fbSMilanka Ringwald     int pos = 0;
520cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
521cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
522cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
523cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
524cf26c8fbSMilanka Ringwald     pos += 2;
525cf26c8fbSMilanka Ringwald     event[pos++] = status;
526fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
527cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
528cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
529cf26c8fbSMilanka Ringwald }
530cf26c8fbSMilanka Ringwald 
531e5451bcdSMatthias Ringwald static void hids_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){
532e5451bcdSMatthias Ringwald     uint8_t event[5];
533e5451bcdSMatthias Ringwald     int pos = 0;
534e5451bcdSMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
535e5451bcdSMatthias Ringwald     event[pos++] = sizeof(event) - 2;
536e5451bcdSMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_DISCONNECTED;
537e5451bcdSMatthias Ringwald     little_endian_store_16(event, pos, cid);
538e5451bcdSMatthias Ringwald     pos += 2;
539e5451bcdSMatthias Ringwald     btstack_assert(pos == sizeof(event));
540e5451bcdSMatthias Ringwald     (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
541e5451bcdSMatthias Ringwald }
542e5451bcdSMatthias Ringwald 
543cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
544cd28e7b1SMilanka Ringwald     uint8_t event[6];
545cd28e7b1SMilanka Ringwald     int pos = 0;
546cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
547cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
548cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
549cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
550cd28e7b1SMilanka Ringwald     pos += 2;
551cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
552cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
553cd28e7b1SMilanka Ringwald }
554cd28e7b1SMilanka Ringwald 
555e3bccb1cSMatthias Ringwald static uint16_t hids_client_setup_report_event(uint8_t subevent, hids_client_t *client, uint8_t report_index, uint8_t *buffer,
556e3bccb1cSMatthias Ringwald                                uint16_t report_len) {
557021192e1SMilanka Ringwald     uint16_t pos = 0;
558021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
559021192e1SMilanka Ringwald     pos++;  // skip len
560e3bccb1cSMatthias Ringwald     buffer[pos++] = subevent;
561021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
562021192e1SMilanka Ringwald     pos += 2;
563021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
564021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
565a2d3931bSMatthias Ringwald     little_endian_store_16(buffer, pos, report_len);
566021192e1SMilanka Ringwald     pos += 2;
567a2d3931bSMatthias Ringwald     buffer[1] = pos + report_len - 2;
568a2d3931bSMatthias Ringwald     return pos;
569a2d3931bSMatthias Ringwald }
57083d7ed1cSMilanka Ringwald 
571a2d3931bSMatthias 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){
572e3bccb1cSMatthias Ringwald     uint16_t pos = hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT, client, report_index, buffer,
573e3bccb1cSMatthias Ringwald                                                   report_len + 1);
574a2d3931bSMatthias Ringwald     buffer[pos] = client->reports[report_index].report_id;
575021192e1SMilanka Ringwald }
576021192e1SMilanka Ringwald 
577f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
578f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
579f4d3b82aSMilanka Ringwald 
580f4d3b82aSMilanka Ringwald     uint8_t event[11];
581f4d3b82aSMilanka Ringwald     int pos = 0;
582f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
583f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
584f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
585f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
586f4d3b82aSMilanka Ringwald     pos += 2;
587f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
588f4d3b82aSMilanka Ringwald 
589f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
590f4d3b82aSMilanka Ringwald     pos += 3;
591f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
592f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
593f4d3b82aSMilanka Ringwald 
594f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
595f4d3b82aSMilanka Ringwald }
596f4d3b82aSMilanka Ringwald 
597af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
598af2241c2SMilanka Ringwald     if (value_len != 1) return;
599af2241c2SMilanka Ringwald 
600af2241c2SMilanka Ringwald     uint8_t event[11];
601af2241c2SMilanka Ringwald     int pos = 0;
602af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
603af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
604af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
605af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
606af2241c2SMilanka Ringwald     pos += 2;
607af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
608af2241c2SMilanka Ringwald     event[pos++] = value[0];
609af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
610af2241c2SMilanka Ringwald }
611f4d3b82aSMilanka Ringwald 
61283d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
613021192e1SMilanka Ringwald     UNUSED(packet_type);
614021192e1SMilanka Ringwald     UNUSED(channel);
61583d7ed1cSMilanka Ringwald 
616021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
617021192e1SMilanka Ringwald 
618021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
61917229983SMatthias Ringwald     if (client == NULL) return;
620021192e1SMilanka Ringwald 
621021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
622021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
623021192e1SMilanka Ringwald         return;
624021192e1SMilanka Ringwald     }
625021192e1SMilanka Ringwald 
626*d41c2d28SMatthias Ringwald     // GATT_EVENT_NOTIFICATION has HID report data at offset 12
627*d41c2d28SMatthias Ringwald     // - see gatt_event_notification_get_value()
628*d41c2d28SMatthias Ringwald     //
629*d41c2d28SMatthias Ringwald     // GATTSERVICE_SUBEVENT_HID_REPORT has HID report data at offset 10
630*d41c2d28SMatthias Ringwald     // - see gattservice_subevent_hid_report_get_report() and add 1 for the inserted Report ID
631*d41c2d28SMatthias Ringwald     //
632*d41c2d28SMatthias Ringwald     // => use existing packet from offset 2 = 12 - 10 to setup event
633*d41c2d28SMatthias Ringwald     const int16_t offset = 2;
634*d41c2d28SMatthias Ringwald 
635*d41c2d28SMatthias Ringwald     uint8_t * in_place_event = &packet[offset];
636a2d3931bSMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
637a2d3931bSMatthias Ringwald                                                   gatt_event_notification_get_value_length(packet));
638*d41c2d28SMatthias Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size - offset);
639021192e1SMilanka Ringwald }
640021192e1SMilanka Ringwald 
64183d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
64283d7ed1cSMilanka Ringwald     UNUSED(packet_type);
64383d7ed1cSMilanka Ringwald     UNUSED(channel);
64483d7ed1cSMilanka Ringwald 
64583d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
64683d7ed1cSMilanka Ringwald 
64783d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
64817229983SMatthias Ringwald     if (client == NULL) return;
64983d7ed1cSMilanka Ringwald 
65083d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
65183d7ed1cSMilanka Ringwald         return;
65283d7ed1cSMilanka Ringwald     }
65383d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
65483d7ed1cSMilanka Ringwald 
65583d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
65683d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
65783d7ed1cSMilanka Ringwald         return;
65883d7ed1cSMilanka Ringwald     }
65983d7ed1cSMilanka Ringwald 
66083d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
66184d4ab66SMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
66284d4ab66SMatthias Ringwald                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
66383d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
66483d7ed1cSMilanka Ringwald }
665cf26c8fbSMilanka Ringwald 
666cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
667cf26c8fbSMilanka Ringwald     uint8_t att_status;
6686bcfb631SMilanka Ringwald     gatt_client_service_t service;
6696bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
670cf26c8fbSMilanka Ringwald 
671cf26c8fbSMilanka Ringwald     switch (client->state){
672cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
673556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
674556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
675556456ccSMilanka Ringwald #endif
676cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
67719146789SMilanka Ringwald 
67819146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
679cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
680cf26c8fbSMilanka Ringwald             UNUSED(att_status);
681cf26c8fbSMilanka Ringwald             break;
682cf26c8fbSMilanka Ringwald 
683cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
684556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
685556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
686556456ccSMilanka Ringwald #endif
6876bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6886bcfb631SMilanka Ringwald 
6896bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6906bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
69119146789SMilanka Ringwald 
69219146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6936bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6946bcfb631SMilanka Ringwald 
6956bcfb631SMilanka Ringwald             UNUSED(att_status);
6966bcfb631SMilanka Ringwald             break;
6976bcfb631SMilanka Ringwald 
69828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
699556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
700556456ccSMilanka 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);
701556456ccSMilanka Ringwald #endif
70228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
703556456ccSMilanka Ringwald 
70419146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
70519146789SMilanka 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);
706e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
707e7bd2dbeSMilanka Ringwald             break;
708e7bd2dbeSMilanka Ringwald 
709e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
710556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
711556456ccSMilanka 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);
712556456ccSMilanka Ringwald #endif
713e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
714e7bd2dbeSMilanka Ringwald 
7153322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
7163322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
7173322b222SMilanka Ringwald 
71819146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
719e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
720e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
721e7bd2dbeSMilanka Ringwald             break;
722e7bd2dbeSMilanka Ringwald 
72328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
724556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
72519146789SMilanka 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);
726556456ccSMilanka Ringwald #endif
72728da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
728e7bd2dbeSMilanka Ringwald 
72919146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
73019146789SMilanka 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);
731e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
732e7bd2dbeSMilanka Ringwald             break;
733e7bd2dbeSMilanka Ringwald 
7343322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
735556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
73619146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
737556456ccSMilanka Ringwald #endif
7383322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
739e7bd2dbeSMilanka Ringwald 
7403322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7413322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
74219146789SMilanka Ringwald 
74319146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7443322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
745e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
746e7bd2dbeSMilanka Ringwald             break;
747e7bd2dbeSMilanka Ringwald 
74828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
749556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
75083d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
751556456ccSMilanka Ringwald                 client->report_index,
75283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
75383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
754556456ccSMilanka Ringwald #endif
75528da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
756af2241c2SMilanka Ringwald             client->handle = 0;
757556456ccSMilanka Ringwald 
758556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
759556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
760556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
761556456ccSMilanka Ringwald 
76219146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
76370093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
76470093cf5SMilanka Ringwald             UNUSED(att_status);
76570093cf5SMilanka Ringwald             break;
76670093cf5SMilanka Ringwald 
76728da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
76828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
76970093cf5SMilanka Ringwald 
77019146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
771af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
772af2241c2SMilanka Ringwald             client->handle = 0;
77370093cf5SMilanka Ringwald             UNUSED(att_status);
77470093cf5SMilanka Ringwald             break;
77570093cf5SMilanka Ringwald 
776cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
777cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
778cd28e7b1SMilanka Ringwald             if (client->value > 0){
779cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
780cd28e7b1SMilanka Ringwald             } else {
781cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
782cd28e7b1SMilanka Ringwald             }
783cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
784cd28e7b1SMilanka Ringwald                 client->report_index,
785cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
786cd28e7b1SMilanka Ringwald #endif
787cd28e7b1SMilanka Ringwald 
788cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
789cd28e7b1SMilanka Ringwald 
790cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
791cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
792cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
793cd28e7b1SMilanka Ringwald 
794cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
795cd28e7b1SMilanka Ringwald 
796cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
797cd28e7b1SMilanka Ringwald 
798cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
799cd28e7b1SMilanka Ringwald                 switch(client->value){
800cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
801cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
802cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
803cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
804cd28e7b1SMilanka Ringwald                         break;
805cd28e7b1SMilanka Ringwald                     default:
806cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
807cd28e7b1SMilanka Ringwald                         break;
808cd28e7b1SMilanka Ringwald                 }
809cd28e7b1SMilanka Ringwald             } else {
810cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
811cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
812cd28e7b1SMilanka Ringwald                     break;
813cd28e7b1SMilanka Ringwald                 }
814cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
815cd28e7b1SMilanka Ringwald             }
816cd28e7b1SMilanka Ringwald             break;
817cd28e7b1SMilanka Ringwald 
818021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
819cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
820cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
821cd28e7b1SMilanka Ringwald                 client->report_index,
822cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
823cd28e7b1SMilanka Ringwald #endif
824021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
825021192e1SMilanka Ringwald 
826021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
827021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
828021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
829021192e1SMilanka Ringwald 
83019146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
831021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
832021192e1SMilanka Ringwald 
833cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
834cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
835cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
836cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
837cd28e7b1SMilanka Ringwald             } else {
838021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
839021192e1SMilanka Ringwald                     hids_run_for_client(client);
840021192e1SMilanka Ringwald                     break;
841021192e1SMilanka Ringwald                 }
842021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
843021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
844021192e1SMilanka Ringwald             }
845021192e1SMilanka Ringwald             break;
846021192e1SMilanka Ringwald 
84783d7ed1cSMilanka Ringwald 
8486d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
84983d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8506d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
85183d7ed1cSMilanka Ringwald                 client->report_index,
85283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
85383d7ed1cSMilanka Ringwald #endif
85483d7ed1cSMilanka Ringwald 
8556d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
85683d7ed1cSMilanka Ringwald 
8576d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8586d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
85984d4ab66SMatthias Ringwald                 &handle_gatt_client_event, client->con_handle,
86083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
86183d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
86283d7ed1cSMilanka Ringwald             UNUSED(att_status);
86383d7ed1cSMilanka Ringwald             break;
86483d7ed1cSMilanka Ringwald 
86583d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
86683d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
867f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
868f4d3b82aSMilanka Ringwald                 client->report_index,
86983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
87083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
87183d7ed1cSMilanka Ringwald #endif
87283d7ed1cSMilanka Ringwald 
87383d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
874f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
87583d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
87683d7ed1cSMilanka Ringwald                 &handle_report_event,
87783d7ed1cSMilanka Ringwald                 client->con_handle,
87883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
87983d7ed1cSMilanka Ringwald             UNUSED(att_status);
88083d7ed1cSMilanka Ringwald             break;
88183d7ed1cSMilanka Ringwald 
88283d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
88383d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
88483d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
88583d7ed1cSMilanka Ringwald 
886f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
88783d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
88883d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
88983d7ed1cSMilanka Ringwald                 client->con_handle,
89083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
89183d7ed1cSMilanka Ringwald 
89283d7ed1cSMilanka Ringwald             break;
89383d7ed1cSMilanka Ringwald #endif
894af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
895af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
89683d7ed1cSMilanka Ringwald 
897f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
898f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
899f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
900f4d3b82aSMilanka Ringwald                 client->con_handle,
901af2241c2SMilanka Ringwald                 client->handle);
902f4d3b82aSMilanka Ringwald             break;
9036d6f7efcSMilanka Ringwald 
904fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
9056d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9061fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
9071fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
9081fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
9091fa7278eSMatthias Ringwald             break;
9101fa7278eSMatthias Ringwald 
9111fa7278eSMatthias Ringwald         default:
9121fa7278eSMatthias Ringwald             break;
9131fa7278eSMatthias Ringwald     }
9141fa7278eSMatthias Ringwald }
9151fa7278eSMatthias Ringwald 
9161fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
9171fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
9181fa7278eSMatthias Ringwald     uint8_t att_status;
9191fa7278eSMatthias Ringwald     switch (client->state){
9201fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
9211fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
9221fa7278eSMatthias Ringwald                 client->con_handle,
9231fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
9241fa7278eSMatthias Ringwald 
9251fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9261fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
9271fa7278eSMatthias Ringwald #endif
9281fa7278eSMatthias Ringwald 
9291fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
9301fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
9311fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
9321fa7278eSMatthias Ringwald                     client->service_index++;
9331fa7278eSMatthias Ringwald                     hids_run_for_client(client);
9341fa7278eSMatthias Ringwald                     break;
9351fa7278eSMatthias Ringwald                 }
9361fa7278eSMatthias Ringwald             }
9371fa7278eSMatthias Ringwald 
9381fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
9391fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
9401fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9411fa7278eSMatthias Ringwald                 break;
9421fa7278eSMatthias Ringwald             }
9431fa7278eSMatthias Ringwald 
9441fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
9451fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
9461fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
9471fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9481fa7278eSMatthias Ringwald                 break;
9491fa7278eSMatthias Ringwald             }
9501fa7278eSMatthias Ringwald 
9511fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9521fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9531fa7278eSMatthias Ringwald             break;
9541fa7278eSMatthias Ringwald 
9551fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9561fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9571fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9581fa7278eSMatthias Ringwald #endif
9591fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9601fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9616d6f7efcSMilanka Ringwald             break;
9626d6f7efcSMilanka Ringwald 
9636bcfb631SMilanka Ringwald         default:
9646bcfb631SMilanka Ringwald             break;
9656bcfb631SMilanka Ringwald     }
9666bcfb631SMilanka Ringwald }
9676bcfb631SMilanka Ringwald 
968cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
969cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
970cf26c8fbSMilanka Ringwald     UNUSED(channel);
971cf26c8fbSMilanka Ringwald     UNUSED(size);
972cf26c8fbSMilanka Ringwald 
9736bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
974b60c1680SMatthias Ringwald     uint8_t status;
9756bcfb631SMilanka Ringwald     gatt_client_service_t service;
9766bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
97770093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
978ab116b1cSMilanka Ringwald 
979021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
980021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
981e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9823322b222SMilanka Ringwald     uint8_t i;
9833322b222SMilanka Ringwald     uint8_t report_index;
984da142a6fSMilanka Ringwald 
985f4d3b82aSMilanka Ringwald     const uint8_t * value;
986f4d3b82aSMilanka Ringwald     uint16_t value_len;
987cf26c8fbSMilanka Ringwald 
988cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
989cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
990cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
99117229983SMatthias Ringwald             if (client == NULL) break;
992cf26c8fbSMilanka Ringwald 
993021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
994021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9956bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
996021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
997021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9986bcfb631SMilanka Ringwald                 client->num_instances++;
999021192e1SMilanka Ringwald 
1000708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1001708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
1002708c69d2SMilanka Ringwald #endif
1003021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
1004021192e1SMilanka Ringwald             }  else {
1005021192e1SMilanka 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);
1006da142a6fSMilanka Ringwald             }
10076bcfb631SMilanka Ringwald             break;
10086bcfb631SMilanka Ringwald 
10096bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
10106bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
101117229983SMatthias Ringwald             if (client == NULL) break;
101217229983SMatthias Ringwald 
10136bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
101419146789SMilanka Ringwald 
101519146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
101619146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
101719146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
101819146789SMilanka Ringwald                     break;
101919146789SMilanka Ringwald                 }
102019146789SMilanka Ringwald             }
102119146789SMilanka Ringwald 
102219146789SMilanka Ringwald             switch (characteristic.uuid16){
102319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1024af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
102519146789SMilanka Ringwald                     break;
102619146789SMilanka Ringwald 
102719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
102819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
102919146789SMilanka Ringwald                     break;
103019146789SMilanka Ringwald 
103119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
103219146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
103319146789SMilanka Ringwald                     break;
103419146789SMilanka Ringwald 
103519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1036f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
103719146789SMilanka Ringwald                     break;
103819146789SMilanka Ringwald 
103919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
104019146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
104119146789SMilanka Ringwald                     break;
104219146789SMilanka Ringwald 
104319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
104419146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
104519146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
104619146789SMilanka Ringwald                     break;
104719146789SMilanka Ringwald 
104819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1049f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
105019146789SMilanka Ringwald                     break;
105119146789SMilanka Ringwald 
105219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1053f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
105419146789SMilanka Ringwald                     break;
105519146789SMilanka Ringwald 
105619146789SMilanka Ringwald                 default:
105719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
105819146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
105919146789SMilanka Ringwald #endif
106019146789SMilanka Ringwald                     return;
106119146789SMilanka Ringwald             }
106219146789SMilanka Ringwald 
106319146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
106419146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
106519146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
106619146789SMilanka Ringwald                 characteristic.start_handle,
106719146789SMilanka Ringwald                 characteristic.properties,
106819146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
106919146789SMilanka Ringwald                 client->service_index);
107019146789SMilanka Ringwald 
107119146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
107219146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
107319146789SMilanka Ringwald             }
107419146789SMilanka Ringwald             printf("\n");
107519146789SMilanka Ringwald #endif
1076cf26c8fbSMilanka Ringwald             break;
1077cf26c8fbSMilanka Ringwald 
10788cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1079e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10808cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
108117229983SMatthias Ringwald             if (client == NULL) break;
1082da142a6fSMilanka Ringwald 
1083f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1084f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10858cec2b74SMilanka Ringwald 
1086556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10878cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1088f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1089556456ccSMilanka Ringwald #endif
1090f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1091f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1092da142a6fSMilanka Ringwald                 if (!stored){
1093da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1094da142a6fSMilanka Ringwald                     break;
1095da142a6fSMilanka Ringwald                 }
1096da142a6fSMilanka Ringwald             }
1097e7bd2dbeSMilanka Ringwald             break;
1098e7bd2dbeSMilanka Ringwald 
109970093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
110070093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
110117229983SMatthias Ringwald             if (client == NULL) break;
110217229983SMatthias Ringwald 
110370093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
110470093cf5SMilanka Ringwald 
1105e7bd2dbeSMilanka Ringwald             switch (client->state) {
1106e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1107e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1108e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
110919146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
11103322b222SMilanka Ringwald 
1111556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1112556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1113556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1114556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1115556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1116556456ccSMilanka Ringwald                                 client->service_index, report_index);
1117556456ccSMilanka Ringwald                         }
1118556456ccSMilanka Ringwald #endif
1119e7bd2dbeSMilanka Ringwald                     }
1120e7bd2dbeSMilanka Ringwald                     break;
112128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
112270093cf5SMilanka Ringwald                     // setup for descriptor value query
112370093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1124af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1125556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1126556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1127556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1128556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
112919146789SMilanka Ringwald #endif
1130556456ccSMilanka Ringwald                     }
1131556456ccSMilanka Ringwald 
113219146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1133556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
113483d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1135556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1136556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1137556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1138556456ccSMilanka Ringwald                     }
1139556456ccSMilanka Ringwald #endif
114070093cf5SMilanka Ringwald                     break;
1141556456ccSMilanka Ringwald 
1142e7bd2dbeSMilanka Ringwald                 default:
1143e7bd2dbeSMilanka Ringwald                     break;
1144e7bd2dbeSMilanka Ringwald             }
1145e7bd2dbeSMilanka Ringwald             break;
114670093cf5SMilanka Ringwald 
114783d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
114883d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
114917229983SMatthias Ringwald             if (client == NULL) break;
115083d7ed1cSMilanka Ringwald 
1151f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1152f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1153f4d3b82aSMilanka Ringwald 
1154af2241c2SMilanka Ringwald 
1155f4d3b82aSMilanka Ringwald             switch (client->state){
1156f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1157f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
115883d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1159f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
116083d7ed1cSMilanka Ringwald                     break;
116183d7ed1cSMilanka Ringwald #endif
1162af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1163af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1164af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1165f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1166f4d3b82aSMilanka Ringwald                         break;
1167af2241c2SMilanka Ringwald                     }
1168af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1169af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1170af2241c2SMilanka Ringwald                         break;
1171af2241c2SMilanka Ringwald                     }
1172af2241c2SMilanka Ringwald                     break;
1173af2241c2SMilanka Ringwald                 }
1174f4d3b82aSMilanka Ringwald                 default:
1175f4d3b82aSMilanka Ringwald                     break;
1176f4d3b82aSMilanka Ringwald             }
1177f4d3b82aSMilanka Ringwald 
1178f4d3b82aSMilanka Ringwald             break;
117983d7ed1cSMilanka Ringwald 
118070093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
118170093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
118217229983SMatthias Ringwald             if (client == NULL) break;
1183e7bd2dbeSMilanka Ringwald 
1184e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1185e7bd2dbeSMilanka Ringwald                 break;
1186e7bd2dbeSMilanka Ringwald             }
11877e1e6e7dSMilanka Ringwald 
1188e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1189e7bd2dbeSMilanka Ringwald             switch (client->state) {
119028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1191e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
119219146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11933322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
119419146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1195556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
119619146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
119719146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1198556456ccSMilanka Ringwald #endif
11993322b222SMilanka Ringwald                     }
1200e7bd2dbeSMilanka Ringwald                     break;
1201e7bd2dbeSMilanka Ringwald 
120228da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
120319146789SMilanka Ringwald 
120419146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1205021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1206021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
120719146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
120883d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1209021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1210021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
121183d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
121219146789SMilanka Ringwald     #endif
121319146789SMilanka Ringwald                     }
1214e7bd2dbeSMilanka Ringwald                     break;
1215e7bd2dbeSMilanka Ringwald 
1216e7bd2dbeSMilanka Ringwald                 default:
1217e7bd2dbeSMilanka Ringwald                     break;
121870093cf5SMilanka Ringwald             }
121970093cf5SMilanka Ringwald             break;
122070093cf5SMilanka Ringwald 
1221cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1222cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
122317229983SMatthias Ringwald             if (client == NULL) break;
1224cf26c8fbSMilanka Ringwald 
122545cccc7dSMatthias Ringwald             status = gatt_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet));
1226cf26c8fbSMilanka Ringwald 
1227cf26c8fbSMilanka Ringwald             switch (client->state){
1228cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1229b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1230b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
12316bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12325fa2c39aSMilanka Ringwald                         return;
12336bcfb631SMilanka Ringwald                     }
12346bcfb631SMilanka Ringwald 
12356bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12366bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12376bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12385fa2c39aSMilanka Ringwald                         return;
12396bcfb631SMilanka Ringwald                     }
12406bcfb631SMilanka Ringwald 
12416bcfb631SMilanka Ringwald                     client->service_index = 0;
124219146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12436bcfb631SMilanka Ringwald                     break;
12446bcfb631SMilanka Ringwald 
12456bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1246b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1247b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
12486bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12495fa2c39aSMilanka Ringwald                         return;
12506bcfb631SMilanka Ringwald                     }
12516bcfb631SMilanka Ringwald 
125270093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
125370093cf5SMilanka Ringwald                         // discover characteristics of next service
125470093cf5SMilanka Ringwald                         client->service_index++;
125570093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1256021192e1SMilanka Ringwald                         break;
1257021192e1SMilanka Ringwald                     }
1258021192e1SMilanka Ringwald 
1259fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1260fd39e93aSMilanka Ringwald 
1261021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1262021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1263fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1264fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1265021192e1SMilanka Ringwald                             }
12667e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12677e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12687e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
126970093cf5SMilanka Ringwald                                 break;
127070093cf5SMilanka Ringwald                             }
127170093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
127270093cf5SMilanka Ringwald                             hids_finalize_client(client);
1273fd39e93aSMilanka Ringwald                             return;
1274fd39e93aSMilanka Ringwald 
1275fd39e93aSMilanka Ringwald                         default:
1276fd39e93aSMilanka Ringwald                             // set boot mode
1277fd39e93aSMilanka Ringwald                             client->service_index = 0;
1278fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1279fd39e93aSMilanka Ringwald                             break;
1280fd39e93aSMilanka Ringwald                     }
12816bcfb631SMilanka Ringwald                     break;
12826bcfb631SMilanka Ringwald 
12833322b222SMilanka Ringwald 
128428da36a6SMilanka Ringwald                 // HID descriptor found
128528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1286b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1287b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
1288e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12895fa2c39aSMilanka Ringwald                         return;
1290e7bd2dbeSMilanka Ringwald                     }
1291e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1292e7bd2dbeSMilanka Ringwald                     break;
1293e7bd2dbeSMilanka Ringwald 
129428da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1295e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12963322b222SMilanka Ringwald                     // go for next report map
12973322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1298e7bd2dbeSMilanka Ringwald                         break;
1299e7bd2dbeSMilanka Ringwald                     }
1300e7bd2dbeSMilanka Ringwald 
13013322b222SMilanka Ringwald                     // read UUIDS for external characteristics
13023322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1303e7bd2dbeSMilanka Ringwald                         break;
1304e7bd2dbeSMilanka Ringwald                     }
1305e7bd2dbeSMilanka Ringwald 
1306e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1307e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13087e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1309e7bd2dbeSMilanka Ringwald                         break;
1310e7bd2dbeSMilanka Ringwald                     }
1311e7bd2dbeSMilanka Ringwald 
1312e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1313e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13145fa2c39aSMilanka Ringwald                     return;
1315e7bd2dbeSMilanka Ringwald 
131628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1317e7bd2dbeSMilanka Ringwald                     // go for next map report
13183322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1319e7bd2dbeSMilanka Ringwald                         break;
1320e7bd2dbeSMilanka Ringwald                     }
1321e7bd2dbeSMilanka Ringwald 
13223322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
13233322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1324e7bd2dbeSMilanka Ringwald                     break;
1325e7bd2dbeSMilanka Ringwald 
13263322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1327e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1328e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13297e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1330e7bd2dbeSMilanka Ringwald                         break;
1331e7bd2dbeSMilanka Ringwald                     }
1332e7bd2dbeSMilanka Ringwald 
1333e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1334e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13355fa2c39aSMilanka Ringwald                     return;
1336e7bd2dbeSMilanka Ringwald 
133728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1338af2241c2SMilanka Ringwald                     if (client->handle != 0){
133928da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
134070093cf5SMilanka Ringwald                         break;
134170093cf5SMilanka Ringwald                     }
134270093cf5SMilanka Ringwald                     // go for next report
13437e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
134470093cf5SMilanka Ringwald                         break;
134570093cf5SMilanka Ringwald                     }
1346835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
134770093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
134870093cf5SMilanka Ringwald                     break;
134970093cf5SMilanka Ringwald 
135028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
135170093cf5SMilanka Ringwald                     // go for next report
13527e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
135370093cf5SMilanka Ringwald                         break;
135470093cf5SMilanka Ringwald                     }
1355021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1356021192e1SMilanka Ringwald                         break;
1357021192e1SMilanka Ringwald                     }
1358835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
135970093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
136070093cf5SMilanka Ringwald                     break;
136170093cf5SMilanka Ringwald 
1362021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1363021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13642901a9b7SMilanka Ringwald                         break;
13652901a9b7SMilanka Ringwald                     }
1366835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13676bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13686bcfb631SMilanka Ringwald                     break;
1369f4d3b82aSMilanka Ringwald 
1370cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1371cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1372cd28e7b1SMilanka Ringwald                         break;
1373cd28e7b1SMilanka Ringwald                     }
1374cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1375cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1376cd28e7b1SMilanka Ringwald                     break;
1377cd28e7b1SMilanka Ringwald 
1378835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1379835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1380835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1381835a13f1SMilanka Ringwald                     break;
1382835a13f1SMilanka Ringwald #endif
1383f4d3b82aSMilanka Ringwald 
1384af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1385f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1386f4d3b82aSMilanka Ringwald                     break;
1387f4d3b82aSMilanka Ringwald 
138884d4ab66SMatthias Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
138984d4ab66SMatthias Ringwald                     {
139084d4ab66SMatthias Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
139184d4ab66SMatthias Ringwald 
139284d4ab66SMatthias Ringwald                         // emit empty report to signal done
139384d4ab66SMatthias Ringwald                         uint8_t event[9];
1394e3bccb1cSMatthias Ringwald                         hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1395e3bccb1cSMatthias Ringwald                                                        client->report_index, event, 0);
139684d4ab66SMatthias Ringwald                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
139784d4ab66SMatthias Ringwald                     }
139884d4ab66SMatthias Ringwald                     break;
13996d6f7efcSMilanka Ringwald 
1400cf26c8fbSMilanka Ringwald                 default:
1401cf26c8fbSMilanka Ringwald                     break;
1402cf26c8fbSMilanka Ringwald             }
1403cf26c8fbSMilanka Ringwald             break;
1404cf26c8fbSMilanka Ringwald 
1405cf26c8fbSMilanka Ringwald         default:
1406cf26c8fbSMilanka Ringwald             break;
1407cf26c8fbSMilanka Ringwald     }
14086bcfb631SMilanka Ringwald 
14096bcfb631SMilanka Ringwald     if (client != NULL){
14106bcfb631SMilanka Ringwald         hids_run_for_client(client);
14116bcfb631SMilanka Ringwald     }
1412cf26c8fbSMilanka Ringwald }
1413cf26c8fbSMilanka Ringwald 
1414e5451bcdSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1415e5451bcdSMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
1416e5451bcdSMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
1417e5451bcdSMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
1418e5451bcdSMatthias Ringwald 
1419e5451bcdSMatthias Ringwald     hci_con_handle_t con_handle;
1420e5451bcdSMatthias Ringwald     hids_client_t * client;
1421e5451bcdSMatthias Ringwald 
1422e5451bcdSMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
1423e5451bcdSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
1424e5451bcdSMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
1425e5451bcdSMatthias Ringwald             client = hids_get_client_for_con_handle(con_handle);
1426e5451bcdSMatthias Ringwald             if (client != NULL){
1427e5451bcdSMatthias Ringwald                 // emit disconnected
1428e5451bcdSMatthias Ringwald                 btstack_packet_handler_t packet_handler = client->client_handler;
1429e5451bcdSMatthias Ringwald                 uint16_t cid = client->cid;
1430e5451bcdSMatthias Ringwald                 hids_emit_disconnected(packet_handler, cid);
1431e5451bcdSMatthias Ringwald                 // finalize
1432e5451bcdSMatthias Ringwald                 hids_finalize_client(client);
1433e5451bcdSMatthias Ringwald             }
1434e5451bcdSMatthias Ringwald             break;
1435e5451bcdSMatthias Ringwald         default:
1436e5451bcdSMatthias Ringwald             break;
1437e5451bcdSMatthias Ringwald     }
1438e5451bcdSMatthias Ringwald }
1439e5451bcdSMatthias Ringwald 
14406bcfb631SMilanka 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){
1441cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1442cf26c8fbSMilanka Ringwald 
1443cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1444cf26c8fbSMilanka Ringwald     if (client != NULL){
1445cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1446cf26c8fbSMilanka Ringwald     }
1447cf26c8fbSMilanka Ringwald 
1448cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1449cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1450cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1451cf26c8fbSMilanka Ringwald     }
1452cf26c8fbSMilanka Ringwald 
1453cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1454cf26c8fbSMilanka Ringwald     if (client == NULL) {
1455cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1456cf26c8fbSMilanka Ringwald     }
1457cf26c8fbSMilanka Ringwald 
14586bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1459cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1460cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1461cf26c8fbSMilanka Ringwald 
1462cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1463cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1464cf26c8fbSMilanka Ringwald }
1465cf26c8fbSMilanka Ringwald 
1466cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1467cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1468cf26c8fbSMilanka Ringwald     if (client == NULL){
1469cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1470cf26c8fbSMilanka Ringwald     }
1471cf26c8fbSMilanka Ringwald     // finalize connection
1472cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1473cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1474cf26c8fbSMilanka Ringwald }
1475cf26c8fbSMilanka Ringwald 
1476fd39e93aSMilanka 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){
14771624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14781624214bSMilanka Ringwald     if (client == NULL){
14791624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14801624214bSMilanka Ringwald     }
14811624214bSMilanka Ringwald 
14821624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14831624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14841624214bSMilanka Ringwald     }
14851624214bSMilanka Ringwald 
1486fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
148783d7ed1cSMilanka Ringwald 
14881624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14891624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14901624214bSMilanka Ringwald     }
14911624214bSMilanka Ringwald 
14921624214bSMilanka Ringwald     uint16_t mtu;
14931624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14941624214bSMilanka Ringwald 
14951624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14961624214bSMilanka Ringwald         return status;
14971624214bSMilanka Ringwald     }
14981624214bSMilanka Ringwald 
14991624214bSMilanka Ringwald     if (mtu - 2 < report_len){
15001624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
15011624214bSMilanka Ringwald     }
15021624214bSMilanka Ringwald 
15036d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1504021192e1SMilanka Ringwald     client->report_index = report_index;
15051624214bSMilanka Ringwald     client->report = report;
15061624214bSMilanka Ringwald     client->report_len = report_len;
15071624214bSMilanka Ringwald 
15081624214bSMilanka Ringwald     hids_run_for_client(client);
15091624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15101624214bSMilanka Ringwald }
15111624214bSMilanka Ringwald 
1512fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
151383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
151483d7ed1cSMilanka Ringwald     if (client == NULL){
151583d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
151683d7ed1cSMilanka Ringwald     }
151783d7ed1cSMilanka Ringwald 
151883d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
151983d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
152083d7ed1cSMilanka Ringwald     }
152183d7ed1cSMilanka Ringwald 
1522fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
152383d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
152483d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
152583d7ed1cSMilanka Ringwald     }
152683d7ed1cSMilanka Ringwald 
152783d7ed1cSMilanka Ringwald     client->report_index = report_index;
152883d7ed1cSMilanka Ringwald 
152983d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
153083d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
153183d7ed1cSMilanka Ringwald #else
153283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
153383d7ed1cSMilanka Ringwald #endif
153483d7ed1cSMilanka Ringwald     hids_run_for_client(client);
153583d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
153683d7ed1cSMilanka Ringwald }
153783d7ed1cSMilanka Ringwald 
153883d7ed1cSMilanka Ringwald 
1539f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1540f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1541f4d3b82aSMilanka Ringwald     if (client == NULL){
1542f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1543f4d3b82aSMilanka Ringwald     }
1544f4d3b82aSMilanka Ringwald 
1545f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1546f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1547f4d3b82aSMilanka Ringwald     }
1548f4d3b82aSMilanka Ringwald 
1549f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1550f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1551f4d3b82aSMilanka Ringwald     }
1552f4d3b82aSMilanka Ringwald 
1553f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1554af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1555af2241c2SMilanka Ringwald 
1556af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1557af2241c2SMilanka Ringwald     hids_run_for_client(client);
1558af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1559af2241c2SMilanka Ringwald }
1560af2241c2SMilanka Ringwald 
1561af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1562af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1563af2241c2SMilanka Ringwald     if (client == NULL){
1564af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1565af2241c2SMilanka Ringwald     }
1566af2241c2SMilanka Ringwald 
1567af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1568af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1569af2241c2SMilanka Ringwald     }
1570af2241c2SMilanka Ringwald 
1571af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1572af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1573af2241c2SMilanka Ringwald     }
1574af2241c2SMilanka Ringwald 
1575af2241c2SMilanka Ringwald     client->service_index = service_index;
1576af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1577af2241c2SMilanka Ringwald 
1578af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1579f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1580f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1581f4d3b82aSMilanka Ringwald }
1582f4d3b82aSMilanka Ringwald 
15830cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15846d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15856d6f7efcSMilanka Ringwald     if (client == NULL){
15866d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15876d6f7efcSMilanka Ringwald     }
15886d6f7efcSMilanka Ringwald 
15896d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15906d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15916d6f7efcSMilanka Ringwald     }
15926d6f7efcSMilanka Ringwald 
15936d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15946d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15956d6f7efcSMilanka Ringwald     }
15966d6f7efcSMilanka Ringwald 
15976d6f7efcSMilanka Ringwald     client->service_index = service_index;
15986d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15996d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
16006d6f7efcSMilanka Ringwald 
16016d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
16026d6f7efcSMilanka Ringwald     hids_run_for_client(client);
16036d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
16046d6f7efcSMilanka Ringwald }
16056d6f7efcSMilanka Ringwald 
16066d6f7efcSMilanka Ringwald 
16076d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
16086d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
16096d6f7efcSMilanka Ringwald     if (client == NULL){
16106d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
16116d6f7efcSMilanka Ringwald     }
16126d6f7efcSMilanka Ringwald 
16136d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
16146d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
16156d6f7efcSMilanka Ringwald     }
16166d6f7efcSMilanka Ringwald 
16176d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
16186d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
16196d6f7efcSMilanka Ringwald     }
16206d6f7efcSMilanka Ringwald 
16216d6f7efcSMilanka Ringwald     client->service_index = service_index;
16226d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
16236d6f7efcSMilanka Ringwald     client->value = value;
16246d6f7efcSMilanka Ringwald 
16256d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
16266d6f7efcSMilanka Ringwald     hids_run_for_client(client);
16276d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
16286d6f7efcSMilanka Ringwald }
16296d6f7efcSMilanka Ringwald 
16306d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
16316d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
16326d6f7efcSMilanka Ringwald }
16336d6f7efcSMilanka Ringwald 
16346d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
16356d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
16366d6f7efcSMilanka Ringwald }
16376d6f7efcSMilanka Ringwald 
16380cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1639cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1640cd28e7b1SMilanka Ringwald     if (client == NULL){
1641cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1642cd28e7b1SMilanka Ringwald     }
1643cd28e7b1SMilanka Ringwald 
1644cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1645cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1646cd28e7b1SMilanka Ringwald     }
1647cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1648cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1649cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1650cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1651cd28e7b1SMilanka Ringwald     }
1652cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1653cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1654cd28e7b1SMilanka Ringwald }
1655cd28e7b1SMilanka Ringwald 
16560cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1657cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1658cd28e7b1SMilanka Ringwald     if (client == NULL){
1659cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1660cd28e7b1SMilanka Ringwald     }
1661cd28e7b1SMilanka Ringwald 
1662cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1663cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1664cd28e7b1SMilanka Ringwald     }
1665cd28e7b1SMilanka Ringwald 
1666cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1667cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1668cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1669cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1670cd28e7b1SMilanka Ringwald     }
1671cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1672cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1673cd28e7b1SMilanka Ringwald }
16746d6f7efcSMilanka Ringwald 
1675021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1676021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1677021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1678e5451bcdSMatthias Ringwald 
1679e5451bcdSMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
1680e5451bcdSMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
1681021192e1SMilanka Ringwald }
1682cf26c8fbSMilanka Ringwald 
1683cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1684