xref: /btstack/src/ble/gatt-service/hids_client.c (revision b60c168034ec65cb9052ba27a808c4f146d10bab)
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"
59*b60c1680SMatthias 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 
62683d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
627a2d3931bSMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
628a2d3931bSMatthias Ringwald                                                   gatt_event_notification_get_value_length(packet));
62983d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
630021192e1SMilanka Ringwald }
631021192e1SMilanka Ringwald 
63283d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
63383d7ed1cSMilanka Ringwald     UNUSED(packet_type);
63483d7ed1cSMilanka Ringwald     UNUSED(channel);
63583d7ed1cSMilanka Ringwald 
63683d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
63783d7ed1cSMilanka Ringwald 
63883d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
63917229983SMatthias Ringwald     if (client == NULL) return;
64083d7ed1cSMilanka Ringwald 
64183d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
64283d7ed1cSMilanka Ringwald         return;
64383d7ed1cSMilanka Ringwald     }
64483d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
64583d7ed1cSMilanka Ringwald 
64683d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
64783d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
64883d7ed1cSMilanka Ringwald         return;
64983d7ed1cSMilanka Ringwald     }
65083d7ed1cSMilanka Ringwald 
65183d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
65284d4ab66SMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
65384d4ab66SMatthias Ringwald                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
65483d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
65583d7ed1cSMilanka Ringwald }
656cf26c8fbSMilanka Ringwald 
657cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
658cf26c8fbSMilanka Ringwald     uint8_t att_status;
6596bcfb631SMilanka Ringwald     gatt_client_service_t service;
6606bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
661cf26c8fbSMilanka Ringwald 
662cf26c8fbSMilanka Ringwald     switch (client->state){
663cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
664556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
665556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
666556456ccSMilanka Ringwald #endif
667cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
66819146789SMilanka Ringwald 
66919146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
670cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
671cf26c8fbSMilanka Ringwald             UNUSED(att_status);
672cf26c8fbSMilanka Ringwald             break;
673cf26c8fbSMilanka Ringwald 
674cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
675556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
676556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
677556456ccSMilanka Ringwald #endif
6786bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6796bcfb631SMilanka Ringwald 
6806bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6816bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
68219146789SMilanka Ringwald 
68319146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6846bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6856bcfb631SMilanka Ringwald 
6866bcfb631SMilanka Ringwald             UNUSED(att_status);
6876bcfb631SMilanka Ringwald             break;
6886bcfb631SMilanka Ringwald 
68928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
690556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
691556456ccSMilanka 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);
692556456ccSMilanka Ringwald #endif
69328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
694556456ccSMilanka Ringwald 
69519146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
69619146789SMilanka 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);
697e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
698e7bd2dbeSMilanka Ringwald             break;
699e7bd2dbeSMilanka Ringwald 
700e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
701556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
702556456ccSMilanka 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);
703556456ccSMilanka Ringwald #endif
704e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
705e7bd2dbeSMilanka Ringwald 
7063322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
7073322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
7083322b222SMilanka Ringwald 
70919146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
710e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
711e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
712e7bd2dbeSMilanka Ringwald             break;
713e7bd2dbeSMilanka Ringwald 
71428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
715556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
71619146789SMilanka 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);
717556456ccSMilanka Ringwald #endif
71828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
719e7bd2dbeSMilanka Ringwald 
72019146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
72119146789SMilanka 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);
722e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
723e7bd2dbeSMilanka Ringwald             break;
724e7bd2dbeSMilanka Ringwald 
7253322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
726556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
72719146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
728556456ccSMilanka Ringwald #endif
7293322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
730e7bd2dbeSMilanka Ringwald 
7313322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7323322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
73319146789SMilanka Ringwald 
73419146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7353322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
736e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
737e7bd2dbeSMilanka Ringwald             break;
738e7bd2dbeSMilanka Ringwald 
73928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
740556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
74183d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
742556456ccSMilanka Ringwald                 client->report_index,
74383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
74483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
745556456ccSMilanka Ringwald #endif
74628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
747af2241c2SMilanka Ringwald             client->handle = 0;
748556456ccSMilanka Ringwald 
749556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
750556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
751556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
752556456ccSMilanka Ringwald 
75319146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
75470093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
75570093cf5SMilanka Ringwald             UNUSED(att_status);
75670093cf5SMilanka Ringwald             break;
75770093cf5SMilanka Ringwald 
75828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
75928da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
76070093cf5SMilanka Ringwald 
76119146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
762af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
763af2241c2SMilanka Ringwald             client->handle = 0;
76470093cf5SMilanka Ringwald             UNUSED(att_status);
76570093cf5SMilanka Ringwald             break;
76670093cf5SMilanka Ringwald 
767cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
768cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
769cd28e7b1SMilanka Ringwald             if (client->value > 0){
770cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
771cd28e7b1SMilanka Ringwald             } else {
772cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
773cd28e7b1SMilanka Ringwald             }
774cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
775cd28e7b1SMilanka Ringwald                 client->report_index,
776cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
777cd28e7b1SMilanka Ringwald #endif
778cd28e7b1SMilanka Ringwald 
779cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
780cd28e7b1SMilanka Ringwald 
781cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
782cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
783cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
784cd28e7b1SMilanka Ringwald 
785cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
786cd28e7b1SMilanka Ringwald 
787cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
788cd28e7b1SMilanka Ringwald 
789cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
790cd28e7b1SMilanka Ringwald                 switch(client->value){
791cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
792cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
793cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
794cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
795cd28e7b1SMilanka Ringwald                         break;
796cd28e7b1SMilanka Ringwald                     default:
797cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
798cd28e7b1SMilanka Ringwald                         break;
799cd28e7b1SMilanka Ringwald                 }
800cd28e7b1SMilanka Ringwald             } else {
801cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
802cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
803cd28e7b1SMilanka Ringwald                     break;
804cd28e7b1SMilanka Ringwald                 }
805cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
806cd28e7b1SMilanka Ringwald             }
807cd28e7b1SMilanka Ringwald             break;
808cd28e7b1SMilanka Ringwald 
809021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
810cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
811cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
812cd28e7b1SMilanka Ringwald                 client->report_index,
813cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
814cd28e7b1SMilanka Ringwald #endif
815021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
816021192e1SMilanka Ringwald 
817021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
818021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
819021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
820021192e1SMilanka Ringwald 
82119146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
822021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
823021192e1SMilanka Ringwald 
824cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
825cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
826cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
827cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
828cd28e7b1SMilanka Ringwald             } else {
829021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
830021192e1SMilanka Ringwald                     hids_run_for_client(client);
831021192e1SMilanka Ringwald                     break;
832021192e1SMilanka Ringwald                 }
833021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
834021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
835021192e1SMilanka Ringwald             }
836021192e1SMilanka Ringwald             break;
837021192e1SMilanka Ringwald 
83883d7ed1cSMilanka Ringwald 
8396d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
84083d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8416d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
84283d7ed1cSMilanka Ringwald                 client->report_index,
84383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
84483d7ed1cSMilanka Ringwald #endif
84583d7ed1cSMilanka Ringwald 
8466d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
84783d7ed1cSMilanka Ringwald 
8486d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8496d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
85084d4ab66SMatthias Ringwald                 &handle_gatt_client_event, client->con_handle,
85183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
85283d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
85383d7ed1cSMilanka Ringwald             UNUSED(att_status);
85483d7ed1cSMilanka Ringwald             break;
85583d7ed1cSMilanka Ringwald 
85683d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
85783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
858f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
859f4d3b82aSMilanka Ringwald                 client->report_index,
86083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
86183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
86283d7ed1cSMilanka Ringwald #endif
86383d7ed1cSMilanka Ringwald 
86483d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
865f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
86683d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
86783d7ed1cSMilanka Ringwald                 &handle_report_event,
86883d7ed1cSMilanka Ringwald                 client->con_handle,
86983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
87083d7ed1cSMilanka Ringwald             UNUSED(att_status);
87183d7ed1cSMilanka Ringwald             break;
87283d7ed1cSMilanka Ringwald 
87383d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
87483d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
87583d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
87683d7ed1cSMilanka Ringwald 
877f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
87883d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
87983d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
88083d7ed1cSMilanka Ringwald                 client->con_handle,
88183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
88283d7ed1cSMilanka Ringwald 
88383d7ed1cSMilanka Ringwald             break;
88483d7ed1cSMilanka Ringwald #endif
885af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
886af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
88783d7ed1cSMilanka Ringwald 
888f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
889f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
890f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
891f4d3b82aSMilanka Ringwald                 client->con_handle,
892af2241c2SMilanka Ringwald                 client->handle);
893f4d3b82aSMilanka Ringwald             break;
8946d6f7efcSMilanka Ringwald 
895fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8966d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
8971fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
8981fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
8991fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
9001fa7278eSMatthias Ringwald             break;
9011fa7278eSMatthias Ringwald 
9021fa7278eSMatthias Ringwald         default:
9031fa7278eSMatthias Ringwald             break;
9041fa7278eSMatthias Ringwald     }
9051fa7278eSMatthias Ringwald }
9061fa7278eSMatthias Ringwald 
9071fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
9081fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
9091fa7278eSMatthias Ringwald     uint8_t att_status;
9101fa7278eSMatthias Ringwald     switch (client->state){
9111fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
9121fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
9131fa7278eSMatthias Ringwald                 client->con_handle,
9141fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
9151fa7278eSMatthias Ringwald 
9161fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9171fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
9181fa7278eSMatthias Ringwald #endif
9191fa7278eSMatthias Ringwald 
9201fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
9211fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
9221fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
9231fa7278eSMatthias Ringwald                     client->service_index++;
9241fa7278eSMatthias Ringwald                     hids_run_for_client(client);
9251fa7278eSMatthias Ringwald                     break;
9261fa7278eSMatthias Ringwald                 }
9271fa7278eSMatthias Ringwald             }
9281fa7278eSMatthias Ringwald 
9291fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
9301fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
9311fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9321fa7278eSMatthias Ringwald                 break;
9331fa7278eSMatthias Ringwald             }
9341fa7278eSMatthias Ringwald 
9351fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
9361fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
9371fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
9381fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9391fa7278eSMatthias Ringwald                 break;
9401fa7278eSMatthias Ringwald             }
9411fa7278eSMatthias Ringwald 
9421fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9431fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9441fa7278eSMatthias Ringwald             break;
9451fa7278eSMatthias Ringwald 
9461fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9471fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9481fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9491fa7278eSMatthias Ringwald #endif
9501fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9511fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9526d6f7efcSMilanka Ringwald             break;
9536d6f7efcSMilanka Ringwald 
9546bcfb631SMilanka Ringwald         default:
9556bcfb631SMilanka Ringwald             break;
9566bcfb631SMilanka Ringwald     }
9576bcfb631SMilanka Ringwald }
9586bcfb631SMilanka Ringwald 
959cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
960cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
961cf26c8fbSMilanka Ringwald     UNUSED(channel);
962cf26c8fbSMilanka Ringwald     UNUSED(size);
963cf26c8fbSMilanka Ringwald 
9646bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
965*b60c1680SMatthias Ringwald     uint8_t status;
9666bcfb631SMilanka Ringwald     gatt_client_service_t service;
9676bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
96870093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
969ab116b1cSMilanka Ringwald 
970021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
971021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
972e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9733322b222SMilanka Ringwald     uint8_t i;
9743322b222SMilanka Ringwald     uint8_t report_index;
975da142a6fSMilanka Ringwald 
976f4d3b82aSMilanka Ringwald     const uint8_t * value;
977f4d3b82aSMilanka Ringwald     uint16_t value_len;
978cf26c8fbSMilanka Ringwald 
979cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
980cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
981cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
98217229983SMatthias Ringwald             if (client == NULL) break;
983cf26c8fbSMilanka Ringwald 
984021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
985021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9866bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
987021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
988021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9896bcfb631SMilanka Ringwald                 client->num_instances++;
990021192e1SMilanka Ringwald 
991708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
992708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
993708c69d2SMilanka Ringwald #endif
994021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
995021192e1SMilanka Ringwald             }  else {
996021192e1SMilanka 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);
997da142a6fSMilanka Ringwald             }
9986bcfb631SMilanka Ringwald             break;
9996bcfb631SMilanka Ringwald 
10006bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
10016bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
100217229983SMatthias Ringwald             if (client == NULL) break;
100317229983SMatthias Ringwald 
10046bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
100519146789SMilanka Ringwald 
100619146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
100719146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
100819146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
100919146789SMilanka Ringwald                     break;
101019146789SMilanka Ringwald                 }
101119146789SMilanka Ringwald             }
101219146789SMilanka Ringwald 
101319146789SMilanka Ringwald             switch (characteristic.uuid16){
101419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1015af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
101619146789SMilanka Ringwald                     break;
101719146789SMilanka Ringwald 
101819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
101919146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
102019146789SMilanka Ringwald                     break;
102119146789SMilanka Ringwald 
102219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
102319146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
102419146789SMilanka Ringwald                     break;
102519146789SMilanka Ringwald 
102619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1027f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
102819146789SMilanka Ringwald                     break;
102919146789SMilanka Ringwald 
103019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
103119146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
103219146789SMilanka Ringwald                     break;
103319146789SMilanka Ringwald 
103419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
103519146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
103619146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
103719146789SMilanka Ringwald                     break;
103819146789SMilanka Ringwald 
103919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1040f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
104119146789SMilanka Ringwald                     break;
104219146789SMilanka Ringwald 
104319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1044f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
104519146789SMilanka Ringwald                     break;
104619146789SMilanka Ringwald 
104719146789SMilanka Ringwald                 default:
104819146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
104919146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
105019146789SMilanka Ringwald #endif
105119146789SMilanka Ringwald                     return;
105219146789SMilanka Ringwald             }
105319146789SMilanka Ringwald 
105419146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
105519146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
105619146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
105719146789SMilanka Ringwald                 characteristic.start_handle,
105819146789SMilanka Ringwald                 characteristic.properties,
105919146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
106019146789SMilanka Ringwald                 client->service_index);
106119146789SMilanka Ringwald 
106219146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
106319146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
106419146789SMilanka Ringwald             }
106519146789SMilanka Ringwald             printf("\n");
106619146789SMilanka Ringwald #endif
1067cf26c8fbSMilanka Ringwald             break;
1068cf26c8fbSMilanka Ringwald 
10698cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1070e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10718cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
107217229983SMatthias Ringwald             if (client == NULL) break;
1073da142a6fSMilanka Ringwald 
1074f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1075f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10768cec2b74SMilanka Ringwald 
1077556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10788cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1079f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1080556456ccSMilanka Ringwald #endif
1081f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1082f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1083da142a6fSMilanka Ringwald                 if (!stored){
1084da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1085da142a6fSMilanka Ringwald                     break;
1086da142a6fSMilanka Ringwald                 }
1087da142a6fSMilanka Ringwald             }
1088e7bd2dbeSMilanka Ringwald             break;
1089e7bd2dbeSMilanka Ringwald 
109070093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
109170093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
109217229983SMatthias Ringwald             if (client == NULL) break;
109317229983SMatthias Ringwald 
109470093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
109570093cf5SMilanka Ringwald 
1096e7bd2dbeSMilanka Ringwald             switch (client->state) {
1097e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1098e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1099e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
110019146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
11013322b222SMilanka Ringwald 
1102556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1103556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1104556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1105556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1106556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1107556456ccSMilanka Ringwald                                 client->service_index, report_index);
1108556456ccSMilanka Ringwald                         }
1109556456ccSMilanka Ringwald #endif
1110e7bd2dbeSMilanka Ringwald                     }
1111e7bd2dbeSMilanka Ringwald                     break;
111228da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
111370093cf5SMilanka Ringwald                     // setup for descriptor value query
111470093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1115af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1116556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1117556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1118556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1119556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
112019146789SMilanka Ringwald #endif
1121556456ccSMilanka Ringwald                     }
1122556456ccSMilanka Ringwald 
112319146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1124556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
112583d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1126556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1127556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1128556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1129556456ccSMilanka Ringwald                     }
1130556456ccSMilanka Ringwald #endif
113170093cf5SMilanka Ringwald                     break;
1132556456ccSMilanka Ringwald 
1133e7bd2dbeSMilanka Ringwald                 default:
1134e7bd2dbeSMilanka Ringwald                     break;
1135e7bd2dbeSMilanka Ringwald             }
1136e7bd2dbeSMilanka Ringwald             break;
113770093cf5SMilanka Ringwald 
113883d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
113983d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
114017229983SMatthias Ringwald             if (client == NULL) break;
114183d7ed1cSMilanka Ringwald 
1142f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1143f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1144f4d3b82aSMilanka Ringwald 
1145af2241c2SMilanka Ringwald 
1146f4d3b82aSMilanka Ringwald             switch (client->state){
1147f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1148f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
114983d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1150f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
115183d7ed1cSMilanka Ringwald                     break;
115283d7ed1cSMilanka Ringwald #endif
1153af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1154af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1155af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1156f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1157f4d3b82aSMilanka Ringwald                         break;
1158af2241c2SMilanka Ringwald                     }
1159af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1160af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1161af2241c2SMilanka Ringwald                         break;
1162af2241c2SMilanka Ringwald                     }
1163af2241c2SMilanka Ringwald                     break;
1164af2241c2SMilanka Ringwald                 }
1165f4d3b82aSMilanka Ringwald                 default:
1166f4d3b82aSMilanka Ringwald                     break;
1167f4d3b82aSMilanka Ringwald             }
1168f4d3b82aSMilanka Ringwald 
1169f4d3b82aSMilanka Ringwald             break;
117083d7ed1cSMilanka Ringwald 
117170093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
117270093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
117317229983SMatthias Ringwald             if (client == NULL) break;
1174e7bd2dbeSMilanka Ringwald 
1175e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1176e7bd2dbeSMilanka Ringwald                 break;
1177e7bd2dbeSMilanka Ringwald             }
11787e1e6e7dSMilanka Ringwald 
1179e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1180e7bd2dbeSMilanka Ringwald             switch (client->state) {
118128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1182e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
118319146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11843322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
118519146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1186556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
118719146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
118819146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1189556456ccSMilanka Ringwald #endif
11903322b222SMilanka Ringwald                     }
1191e7bd2dbeSMilanka Ringwald                     break;
1192e7bd2dbeSMilanka Ringwald 
119328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
119419146789SMilanka Ringwald 
119519146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1196021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1197021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
119819146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
119983d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1200021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1201021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
120283d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
120319146789SMilanka Ringwald     #endif
120419146789SMilanka Ringwald                     }
1205e7bd2dbeSMilanka Ringwald                     break;
1206e7bd2dbeSMilanka Ringwald 
1207e7bd2dbeSMilanka Ringwald                 default:
1208e7bd2dbeSMilanka Ringwald                     break;
120970093cf5SMilanka Ringwald             }
121070093cf5SMilanka Ringwald             break;
121170093cf5SMilanka Ringwald 
1212cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1213cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
121417229983SMatthias Ringwald             if (client == NULL) break;
1215cf26c8fbSMilanka Ringwald 
1216*b60c1680SMatthias Ringwald             status = gatt_service_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet));
1217cf26c8fbSMilanka Ringwald 
1218cf26c8fbSMilanka Ringwald             switch (client->state){
1219cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1220*b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1221*b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
12226bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12235fa2c39aSMilanka Ringwald                         return;
12246bcfb631SMilanka Ringwald                     }
12256bcfb631SMilanka Ringwald 
12266bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12276bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12286bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12295fa2c39aSMilanka Ringwald                         return;
12306bcfb631SMilanka Ringwald                     }
12316bcfb631SMilanka Ringwald 
12326bcfb631SMilanka Ringwald                     client->service_index = 0;
123319146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12346bcfb631SMilanka Ringwald                     break;
12356bcfb631SMilanka Ringwald 
12366bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1237*b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1238*b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
12396bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12405fa2c39aSMilanka Ringwald                         return;
12416bcfb631SMilanka Ringwald                     }
12426bcfb631SMilanka Ringwald 
124370093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
124470093cf5SMilanka Ringwald                         // discover characteristics of next service
124570093cf5SMilanka Ringwald                         client->service_index++;
124670093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1247021192e1SMilanka Ringwald                         break;
1248021192e1SMilanka Ringwald                     }
1249021192e1SMilanka Ringwald 
1250fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1251fd39e93aSMilanka Ringwald 
1252021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1253021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1254fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1255fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1256021192e1SMilanka Ringwald                             }
12577e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12587e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12597e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
126070093cf5SMilanka Ringwald                                 break;
126170093cf5SMilanka Ringwald                             }
126270093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
126370093cf5SMilanka Ringwald                             hids_finalize_client(client);
1264fd39e93aSMilanka Ringwald                             return;
1265fd39e93aSMilanka Ringwald 
1266fd39e93aSMilanka Ringwald                         default:
1267fd39e93aSMilanka Ringwald                             // set boot mode
1268fd39e93aSMilanka Ringwald                             client->service_index = 0;
1269fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1270fd39e93aSMilanka Ringwald                             break;
1271fd39e93aSMilanka Ringwald                     }
12726bcfb631SMilanka Ringwald                     break;
12736bcfb631SMilanka Ringwald 
12743322b222SMilanka Ringwald 
127528da36a6SMilanka Ringwald                 // HID descriptor found
127628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1277*b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1278*b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
1279e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12805fa2c39aSMilanka Ringwald                         return;
1281e7bd2dbeSMilanka Ringwald                     }
1282e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1283e7bd2dbeSMilanka Ringwald                     break;
1284e7bd2dbeSMilanka Ringwald 
128528da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1286e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12873322b222SMilanka Ringwald                     // go for next report map
12883322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1289e7bd2dbeSMilanka Ringwald                         break;
1290e7bd2dbeSMilanka Ringwald                     }
1291e7bd2dbeSMilanka Ringwald 
12923322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12933322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1294e7bd2dbeSMilanka Ringwald                         break;
1295e7bd2dbeSMilanka Ringwald                     }
1296e7bd2dbeSMilanka Ringwald 
1297e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1298e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12997e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1300e7bd2dbeSMilanka Ringwald                         break;
1301e7bd2dbeSMilanka Ringwald                     }
1302e7bd2dbeSMilanka Ringwald 
1303e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1304e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13055fa2c39aSMilanka Ringwald                     return;
1306e7bd2dbeSMilanka Ringwald 
130728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1308e7bd2dbeSMilanka Ringwald                     // go for next map report
13093322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1310e7bd2dbeSMilanka Ringwald                         break;
1311e7bd2dbeSMilanka Ringwald                     }
1312e7bd2dbeSMilanka Ringwald 
13133322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
13143322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1315e7bd2dbeSMilanka Ringwald                     break;
1316e7bd2dbeSMilanka Ringwald 
13173322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1318e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1319e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13207e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1321e7bd2dbeSMilanka Ringwald                         break;
1322e7bd2dbeSMilanka Ringwald                     }
1323e7bd2dbeSMilanka Ringwald 
1324e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1325e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13265fa2c39aSMilanka Ringwald                     return;
1327e7bd2dbeSMilanka Ringwald 
132828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1329af2241c2SMilanka Ringwald                     if (client->handle != 0){
133028da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
133170093cf5SMilanka Ringwald                         break;
133270093cf5SMilanka Ringwald                     }
133370093cf5SMilanka Ringwald                     // go for next report
13347e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
133570093cf5SMilanka Ringwald                         break;
133670093cf5SMilanka Ringwald                     }
1337835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
133870093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
133970093cf5SMilanka Ringwald                     break;
134070093cf5SMilanka Ringwald 
134128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
134270093cf5SMilanka Ringwald                     // go for next report
13437e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
134470093cf5SMilanka Ringwald                         break;
134570093cf5SMilanka Ringwald                     }
1346021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1347021192e1SMilanka Ringwald                         break;
1348021192e1SMilanka Ringwald                     }
1349835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
135070093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
135170093cf5SMilanka Ringwald                     break;
135270093cf5SMilanka Ringwald 
1353021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1354021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13552901a9b7SMilanka Ringwald                         break;
13562901a9b7SMilanka Ringwald                     }
1357835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13586bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13596bcfb631SMilanka Ringwald                     break;
1360f4d3b82aSMilanka Ringwald 
1361cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1362cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1363cd28e7b1SMilanka Ringwald                         break;
1364cd28e7b1SMilanka Ringwald                     }
1365cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1366cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1367cd28e7b1SMilanka Ringwald                     break;
1368cd28e7b1SMilanka Ringwald 
1369835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1370835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1371835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1372835a13f1SMilanka Ringwald                     break;
1373835a13f1SMilanka Ringwald #endif
1374f4d3b82aSMilanka Ringwald 
1375af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1376f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1377f4d3b82aSMilanka Ringwald                     break;
1378f4d3b82aSMilanka Ringwald 
137984d4ab66SMatthias Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
138084d4ab66SMatthias Ringwald                     {
138184d4ab66SMatthias Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
138284d4ab66SMatthias Ringwald 
138384d4ab66SMatthias Ringwald                         // emit empty report to signal done
138484d4ab66SMatthias Ringwald                         uint8_t event[9];
1385e3bccb1cSMatthias Ringwald                         hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1386e3bccb1cSMatthias Ringwald                                                        client->report_index, event, 0);
138784d4ab66SMatthias Ringwald                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
138884d4ab66SMatthias Ringwald                     }
138984d4ab66SMatthias Ringwald                     break;
13906d6f7efcSMilanka Ringwald 
1391cf26c8fbSMilanka Ringwald                 default:
1392cf26c8fbSMilanka Ringwald                     break;
1393cf26c8fbSMilanka Ringwald             }
1394cf26c8fbSMilanka Ringwald             break;
1395cf26c8fbSMilanka Ringwald 
1396cf26c8fbSMilanka Ringwald         default:
1397cf26c8fbSMilanka Ringwald             break;
1398cf26c8fbSMilanka Ringwald     }
13996bcfb631SMilanka Ringwald 
14006bcfb631SMilanka Ringwald     if (client != NULL){
14016bcfb631SMilanka Ringwald         hids_run_for_client(client);
14026bcfb631SMilanka Ringwald     }
1403cf26c8fbSMilanka Ringwald }
1404cf26c8fbSMilanka Ringwald 
1405e5451bcdSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1406e5451bcdSMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
1407e5451bcdSMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
1408e5451bcdSMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
1409e5451bcdSMatthias Ringwald 
1410e5451bcdSMatthias Ringwald     hci_con_handle_t con_handle;
1411e5451bcdSMatthias Ringwald     hids_client_t * client;
1412e5451bcdSMatthias Ringwald 
1413e5451bcdSMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
1414e5451bcdSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
1415e5451bcdSMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
1416e5451bcdSMatthias Ringwald             client = hids_get_client_for_con_handle(con_handle);
1417e5451bcdSMatthias Ringwald             if (client != NULL){
1418e5451bcdSMatthias Ringwald                 // emit disconnected
1419e5451bcdSMatthias Ringwald                 btstack_packet_handler_t packet_handler = client->client_handler;
1420e5451bcdSMatthias Ringwald                 uint16_t cid = client->cid;
1421e5451bcdSMatthias Ringwald                 hids_emit_disconnected(packet_handler, cid);
1422e5451bcdSMatthias Ringwald                 // finalize
1423e5451bcdSMatthias Ringwald                 hids_finalize_client(client);
1424e5451bcdSMatthias Ringwald             }
1425e5451bcdSMatthias Ringwald             break;
1426e5451bcdSMatthias Ringwald         default:
1427e5451bcdSMatthias Ringwald             break;
1428e5451bcdSMatthias Ringwald     }
1429e5451bcdSMatthias Ringwald }
1430e5451bcdSMatthias Ringwald 
14316bcfb631SMilanka 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){
1432cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1433cf26c8fbSMilanka Ringwald 
1434cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1435cf26c8fbSMilanka Ringwald     if (client != NULL){
1436cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1437cf26c8fbSMilanka Ringwald     }
1438cf26c8fbSMilanka Ringwald 
1439cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1440cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1441cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1442cf26c8fbSMilanka Ringwald     }
1443cf26c8fbSMilanka Ringwald 
1444cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1445cf26c8fbSMilanka Ringwald     if (client == NULL) {
1446cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1447cf26c8fbSMilanka Ringwald     }
1448cf26c8fbSMilanka Ringwald 
14496bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1450cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1451cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1452cf26c8fbSMilanka Ringwald 
1453cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1454cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1455cf26c8fbSMilanka Ringwald }
1456cf26c8fbSMilanka Ringwald 
1457cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1458cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1459cf26c8fbSMilanka Ringwald     if (client == NULL){
1460cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1461cf26c8fbSMilanka Ringwald     }
1462cf26c8fbSMilanka Ringwald     // finalize connection
1463cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1464cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1465cf26c8fbSMilanka Ringwald }
1466cf26c8fbSMilanka Ringwald 
1467fd39e93aSMilanka 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){
14681624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14691624214bSMilanka Ringwald     if (client == NULL){
14701624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14711624214bSMilanka Ringwald     }
14721624214bSMilanka Ringwald 
14731624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14741624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14751624214bSMilanka Ringwald     }
14761624214bSMilanka Ringwald 
1477fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
147883d7ed1cSMilanka Ringwald 
14791624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14801624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14811624214bSMilanka Ringwald     }
14821624214bSMilanka Ringwald 
14831624214bSMilanka Ringwald     uint16_t mtu;
14841624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14851624214bSMilanka Ringwald 
14861624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14871624214bSMilanka Ringwald         return status;
14881624214bSMilanka Ringwald     }
14891624214bSMilanka Ringwald 
14901624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14911624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14921624214bSMilanka Ringwald     }
14931624214bSMilanka Ringwald 
14946d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1495021192e1SMilanka Ringwald     client->report_index = report_index;
14961624214bSMilanka Ringwald     client->report = report;
14971624214bSMilanka Ringwald     client->report_len = report_len;
14981624214bSMilanka Ringwald 
14991624214bSMilanka Ringwald     hids_run_for_client(client);
15001624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15011624214bSMilanka Ringwald }
15021624214bSMilanka Ringwald 
1503fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
150483d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
150583d7ed1cSMilanka Ringwald     if (client == NULL){
150683d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
150783d7ed1cSMilanka Ringwald     }
150883d7ed1cSMilanka Ringwald 
150983d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
151083d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
151183d7ed1cSMilanka Ringwald     }
151283d7ed1cSMilanka Ringwald 
1513fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
151483d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
151583d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
151683d7ed1cSMilanka Ringwald     }
151783d7ed1cSMilanka Ringwald 
151883d7ed1cSMilanka Ringwald     client->report_index = report_index;
151983d7ed1cSMilanka Ringwald 
152083d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
152183d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
152283d7ed1cSMilanka Ringwald #else
152383d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
152483d7ed1cSMilanka Ringwald #endif
152583d7ed1cSMilanka Ringwald     hids_run_for_client(client);
152683d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
152783d7ed1cSMilanka Ringwald }
152883d7ed1cSMilanka Ringwald 
152983d7ed1cSMilanka Ringwald 
1530f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1531f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1532f4d3b82aSMilanka Ringwald     if (client == NULL){
1533f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1534f4d3b82aSMilanka Ringwald     }
1535f4d3b82aSMilanka Ringwald 
1536f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1537f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1538f4d3b82aSMilanka Ringwald     }
1539f4d3b82aSMilanka Ringwald 
1540f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1541f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1542f4d3b82aSMilanka Ringwald     }
1543f4d3b82aSMilanka Ringwald 
1544f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1545af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1546af2241c2SMilanka Ringwald 
1547af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1548af2241c2SMilanka Ringwald     hids_run_for_client(client);
1549af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1550af2241c2SMilanka Ringwald }
1551af2241c2SMilanka Ringwald 
1552af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1553af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1554af2241c2SMilanka Ringwald     if (client == NULL){
1555af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1556af2241c2SMilanka Ringwald     }
1557af2241c2SMilanka Ringwald 
1558af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1559af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1560af2241c2SMilanka Ringwald     }
1561af2241c2SMilanka Ringwald 
1562af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1563af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1564af2241c2SMilanka Ringwald     }
1565af2241c2SMilanka Ringwald 
1566af2241c2SMilanka Ringwald     client->service_index = service_index;
1567af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1568af2241c2SMilanka Ringwald 
1569af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1570f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1571f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1572f4d3b82aSMilanka Ringwald }
1573f4d3b82aSMilanka Ringwald 
15740cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15756d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15766d6f7efcSMilanka Ringwald     if (client == NULL){
15776d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15786d6f7efcSMilanka Ringwald     }
15796d6f7efcSMilanka Ringwald 
15806d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15816d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15826d6f7efcSMilanka Ringwald     }
15836d6f7efcSMilanka Ringwald 
15846d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15856d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15866d6f7efcSMilanka Ringwald     }
15876d6f7efcSMilanka Ringwald 
15886d6f7efcSMilanka Ringwald     client->service_index = service_index;
15896d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15906d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15916d6f7efcSMilanka Ringwald 
15926d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15936d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15946d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15956d6f7efcSMilanka Ringwald }
15966d6f7efcSMilanka Ringwald 
15976d6f7efcSMilanka Ringwald 
15986d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15996d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
16006d6f7efcSMilanka Ringwald     if (client == NULL){
16016d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
16026d6f7efcSMilanka Ringwald     }
16036d6f7efcSMilanka Ringwald 
16046d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
16056d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
16066d6f7efcSMilanka Ringwald     }
16076d6f7efcSMilanka Ringwald 
16086d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
16096d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
16106d6f7efcSMilanka Ringwald     }
16116d6f7efcSMilanka Ringwald 
16126d6f7efcSMilanka Ringwald     client->service_index = service_index;
16136d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
16146d6f7efcSMilanka Ringwald     client->value = value;
16156d6f7efcSMilanka Ringwald 
16166d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
16176d6f7efcSMilanka Ringwald     hids_run_for_client(client);
16186d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
16196d6f7efcSMilanka Ringwald }
16206d6f7efcSMilanka Ringwald 
16216d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
16226d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
16236d6f7efcSMilanka Ringwald }
16246d6f7efcSMilanka Ringwald 
16256d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
16266d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
16276d6f7efcSMilanka Ringwald }
16286d6f7efcSMilanka Ringwald 
16290cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1630cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1631cd28e7b1SMilanka Ringwald     if (client == NULL){
1632cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1633cd28e7b1SMilanka Ringwald     }
1634cd28e7b1SMilanka Ringwald 
1635cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1636cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1637cd28e7b1SMilanka Ringwald     }
1638cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1639cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1640cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1641cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1642cd28e7b1SMilanka Ringwald     }
1643cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1644cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1645cd28e7b1SMilanka Ringwald }
1646cd28e7b1SMilanka Ringwald 
16470cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1648cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1649cd28e7b1SMilanka Ringwald     if (client == NULL){
1650cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1651cd28e7b1SMilanka Ringwald     }
1652cd28e7b1SMilanka Ringwald 
1653cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1654cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1655cd28e7b1SMilanka Ringwald     }
1656cd28e7b1SMilanka Ringwald 
1657cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1658cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1659cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1660cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1661cd28e7b1SMilanka Ringwald     }
1662cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1663cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1664cd28e7b1SMilanka Ringwald }
16656d6f7efcSMilanka Ringwald 
1666021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1667021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1668021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1669e5451bcdSMatthias Ringwald 
1670e5451bcdSMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
1671e5451bcdSMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
1672021192e1SMilanka Ringwald }
1673cf26c8fbSMilanka Ringwald 
1674cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1675