xref: /btstack/src/ble/gatt-service/hids_client.c (revision e5451bcdc8dfacd67b2eb41aaff84cb38caf2254)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/core.h"
53fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
54cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
55fc975d0eSMilanka Ringwald #include "btstack_debug.h"
56fc975d0eSMilanka Ringwald #include "btstack_event.h"
57fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
58fc975d0eSMilanka Ringwald #include "gap.h"
59fc975d0eSMilanka Ringwald 
6070093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6470093cf5SMilanka Ringwald 
65*e5451bcdSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
66*e5451bcdSMatthias Ringwald 
67da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
68da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
69da142a6fSMilanka Ringwald 
70da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
71da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
72da142a6fSMilanka Ringwald 
73cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
741fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context);
75cf26c8fbSMilanka Ringwald 
76556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
77556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
78556456ccSMilanka Ringwald     switch (uuid){
79556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
80556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
81556456ccSMilanka Ringwald 
82556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
83556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
84556456ccSMilanka Ringwald 
85556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
86556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
87556456ccSMilanka Ringwald 
88556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
89556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
90556456ccSMilanka Ringwald 
91556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
92556456ccSMilanka Ringwald             return "REPORT";
93556456ccSMilanka Ringwald 
94556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
95556456ccSMilanka Ringwald             return "REPORT_MAP";
96556456ccSMilanka Ringwald 
97556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
98556456ccSMilanka Ringwald             return "HID_INFORMATION";
99556456ccSMilanka Ringwald 
100556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
101556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
102556456ccSMilanka Ringwald         default:
103556456ccSMilanka Ringwald             return "UKNOWN";
104556456ccSMilanka Ringwald     }
105556456ccSMilanka Ringwald }
106556456ccSMilanka Ringwald #endif
107556456ccSMilanka Ringwald 
108da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
109da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
110da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
111da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
112da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
113da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
114da142a6fSMilanka Ringwald         return client;
115da142a6fSMilanka Ringwald     }
116da142a6fSMilanka Ringwald     return NULL;
117da142a6fSMilanka Ringwald }
118da142a6fSMilanka Ringwald 
119da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
120da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
121da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
122da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
123da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
124da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
125da142a6fSMilanka Ringwald         return client;
126da142a6fSMilanka Ringwald     }
127da142a6fSMilanka Ringwald     return NULL;
128da142a6fSMilanka Ringwald }
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald 
131da142a6fSMilanka Ringwald // START Descriptor Storage Util
132da142a6fSMilanka Ringwald 
133a391be9aSMatthias Ringwald static uint16_t hids_client_descriptors_len(hids_client_t * client){
134a391be9aSMatthias Ringwald     uint16_t descriptors_len = 0;
135a391be9aSMatthias Ringwald     uint8_t service_index;
136a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
137a391be9aSMatthias Ringwald         descriptors_len += client->services[service_index].hid_descriptor_len;
138a391be9aSMatthias Ringwald     }
139a391be9aSMatthias Ringwald     return descriptors_len;
140a391be9aSMatthias Ringwald }
141a391be9aSMatthias Ringwald 
142da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
143da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
144da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
145da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
146da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
147da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
148da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
149a391be9aSMatthias Ringwald         free_space -= hids_client_descriptors_len(client);
150da142a6fSMilanka Ringwald     }
151da142a6fSMilanka Ringwald     return free_space;
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
155a391be9aSMatthias Ringwald     // reserve remaining space for this connection
156a391be9aSMatthias Ringwald     uint16_t available_space = hids_client_descriptor_storage_get_available_space();
157da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
158a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_max_len = available_space;
159a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
160da142a6fSMilanka Ringwald }
161da142a6fSMilanka Ringwald 
162da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
163a391be9aSMatthias Ringwald     // store single hid descriptor byte
164da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
165da142a6fSMilanka Ringwald 
166da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
167da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
168da142a6fSMilanka Ringwald     return true;
169da142a6fSMilanka Ringwald }
170da142a6fSMilanka Ringwald 
171da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
172a391be9aSMatthias Ringwald     uint8_t service_index;
173da142a6fSMilanka Ringwald 
174a391be9aSMatthias Ringwald     // calculate descriptors len
175a391be9aSMatthias Ringwald     uint16_t descriptors_len = hids_client_descriptors_len(client);
176da142a6fSMilanka Ringwald 
177a391be9aSMatthias Ringwald     if (descriptors_len > 0){
178a391be9aSMatthias Ringwald         // move higher descriptors down
179a391be9aSMatthias Ringwald         uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
180da142a6fSMilanka Ringwald         memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
181da142a6fSMilanka Ringwald                 &hids_client_descriptor_storage[next_offset],
182da142a6fSMilanka Ringwald                 hids_client_descriptor_storage_len - next_offset);
183da142a6fSMilanka Ringwald 
184a391be9aSMatthias Ringwald         // fix descriptor offset of higher descriptors
185da142a6fSMilanka Ringwald         btstack_linked_list_iterator_t it;
186da142a6fSMilanka Ringwald         btstack_linked_list_iterator_init(&it, &clients);
187da142a6fSMilanka Ringwald         while (btstack_linked_list_iterator_has_next(&it)){
188da142a6fSMilanka Ringwald             hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
189a391be9aSMatthias Ringwald             if (conn == client) continue;
190a391be9aSMatthias Ringwald             for (service_index = 0; service_index < client->num_instances; service_index++){
191a391be9aSMatthias Ringwald                 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
192a391be9aSMatthias Ringwald                     conn->services[service_index].hid_descriptor_offset -= descriptors_len;
193da142a6fSMilanka Ringwald                 }
194da142a6fSMilanka Ringwald             }
195da142a6fSMilanka Ringwald         }
196da142a6fSMilanka Ringwald     }
197da142a6fSMilanka Ringwald 
198a391be9aSMatthias Ringwald     // clear descriptors
199a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
200a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_len = 0;
201a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_offset = 0;
202a391be9aSMatthias Ringwald     }
203a391be9aSMatthias Ringwald }
204a391be9aSMatthias Ringwald 
205da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
206da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
207021192e1SMilanka Ringwald     if (client == NULL){
208021192e1SMilanka Ringwald         return NULL;
209021192e1SMilanka Ringwald     }
210021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
211da142a6fSMilanka Ringwald         return NULL;
212da142a6fSMilanka Ringwald     }
213da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
214da142a6fSMilanka Ringwald }
215da142a6fSMilanka Ringwald 
216da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
217da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
218021192e1SMilanka Ringwald     if (client == NULL){
219021192e1SMilanka Ringwald         return 0;
220021192e1SMilanka Ringwald     }
221021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
222da142a6fSMilanka Ringwald         return 0;
223da142a6fSMilanka Ringwald     }
224da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
225da142a6fSMilanka Ringwald }
226da142a6fSMilanka Ringwald 
227da142a6fSMilanka Ringwald // END Descriptor Storage Util
228da142a6fSMilanka Ringwald 
229cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
230cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
231cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
232cf26c8fbSMilanka Ringwald     } else {
233cf26c8fbSMilanka Ringwald         hids_cid_counter++;
234cf26c8fbSMilanka Ringwald     }
235cf26c8fbSMilanka Ringwald     return hids_cid_counter;
236fc975d0eSMilanka Ringwald }
237fc975d0eSMilanka Ringwald 
238ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
239ab116b1cSMilanka Ringwald     uint8_t i;
240a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
241ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
242ab116b1cSMilanka Ringwald             return i;
243ab116b1cSMilanka Ringwald         }
244ab116b1cSMilanka Ringwald     }
245ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
246ab116b1cSMilanka Ringwald }
247ab116b1cSMilanka Ringwald 
24819146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
24919146789SMilanka Ringwald     uint8_t i;
25019146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
25119146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
25219146789SMilanka Ringwald             return i;
25319146789SMilanka Ringwald         }
25419146789SMilanka Ringwald     }
25519146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
25619146789SMilanka Ringwald }
25719146789SMilanka Ringwald 
25819146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
25919146789SMilanka Ringwald     uint8_t i;
26019146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
26119146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
26219146789SMilanka Ringwald             return true;
26319146789SMilanka Ringwald         }
26419146789SMilanka Ringwald     }
26519146789SMilanka Ringwald     return false;
26619146789SMilanka Ringwald }
26719146789SMilanka Ringwald 
268fd39e93aSMilanka 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){
26983d7ed1cSMilanka Ringwald     uint8_t i;
270835a13f1SMilanka Ringwald 
27183d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
272fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
273fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
274fd39e93aSMilanka Ringwald 
275fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
27683d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
27783d7ed1cSMilanka Ringwald                 continue;
27883d7ed1cSMilanka Ringwald             }
279fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
28083d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
28183d7ed1cSMilanka Ringwald                 continue;
28283d7ed1cSMilanka Ringwald             }
283fd39e93aSMilanka Ringwald         }
284fd39e93aSMilanka Ringwald 
285fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
28683d7ed1cSMilanka Ringwald             return i;
28783d7ed1cSMilanka Ringwald         }
28883d7ed1cSMilanka Ringwald     }
28983d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
29083d7ed1cSMilanka Ringwald }
29119146789SMilanka Ringwald 
292021192e1SMilanka 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){
293ab116b1cSMilanka Ringwald 
29419146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
295ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2963322b222SMilanka Ringwald         return report_index;
297ab116b1cSMilanka Ringwald     }
29828da36a6SMilanka Ringwald     report_index = client->num_reports;
299ab116b1cSMilanka Ringwald 
30028da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
30128da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
30228da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
30328da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
304ab116b1cSMilanka Ringwald 
30528da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
30628da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
30728da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
308021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
30970093cf5SMilanka Ringwald 
310021192e1SMilanka 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);
311ab116b1cSMilanka Ringwald         client->num_reports++;
3123322b222SMilanka Ringwald         return report_index;
313ab116b1cSMilanka Ringwald     } else {
314ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3153322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
316ab116b1cSMilanka Ringwald     }
317ab116b1cSMilanka Ringwald }
318ab116b1cSMilanka Ringwald 
31919146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
320556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
321556456ccSMilanka Ringwald 
322556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
32319146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
324556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
32519146789SMilanka Ringwald 
326556456ccSMilanka Ringwald         client->num_external_reports++;
32719146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
328556456ccSMilanka Ringwald         return report_index;
329556456ccSMilanka Ringwald     } else {
330556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
331556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
33270093cf5SMilanka Ringwald     }
333556456ccSMilanka Ringwald }
334556456ccSMilanka Ringwald 
3357e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3367e1e6e7dSMilanka Ringwald     uint8_t i;
3373322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3383322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
339556456ccSMilanka Ringwald             return i;
3403322b222SMilanka Ringwald         }
3413322b222SMilanka Ringwald     }
342556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
343556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3443322b222SMilanka Ringwald }
3453322b222SMilanka Ringwald 
3463322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3473322b222SMilanka Ringwald     client->service_index++;
3483322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
349556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3503322b222SMilanka Ringwald         return true;
3513322b222SMilanka Ringwald     }
3523322b222SMilanka Ringwald     return false;
3533322b222SMilanka Ringwald }
3543322b222SMilanka Ringwald 
3553322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3563322b222SMilanka Ringwald     client->service_index = 0;
3573322b222SMilanka Ringwald 
3583322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3593322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3603322b222SMilanka Ringwald         return true;
3613322b222SMilanka Ringwald     }
3623322b222SMilanka Ringwald     return false;
3633322b222SMilanka Ringwald }
3643322b222SMilanka Ringwald 
3653322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
366021192e1SMilanka Ringwald     client->report_index++;
36719146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3683322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
369e7bd2dbeSMilanka Ringwald         return true;
370e7bd2dbeSMilanka Ringwald     }
371e7bd2dbeSMilanka Ringwald     return false;
372e7bd2dbeSMilanka Ringwald }
373e7bd2dbeSMilanka Ringwald 
3743322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
375021192e1SMilanka Ringwald     client->report_index = 0;
37619146789SMilanka Ringwald     if (client->num_external_reports > 0){
3773322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3787e1e6e7dSMilanka Ringwald         return true;
3797e1e6e7dSMilanka Ringwald     }
3807e1e6e7dSMilanka Ringwald     return false;
3817e1e6e7dSMilanka Ringwald }
3827e1e6e7dSMilanka Ringwald 
3837e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3847e1e6e7dSMilanka Ringwald     uint8_t i;
3857e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
386fd39e93aSMilanka Ringwald 
387021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
38870093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
389fd39e93aSMilanka Ringwald         if (!report.boot_report){
39070093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3917e1e6e7dSMilanka Ringwald                 index = i;
392021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3937e1e6e7dSMilanka Ringwald                 break;
39470093cf5SMilanka Ringwald             }
395fd39e93aSMilanka Ringwald         }
396fd39e93aSMilanka Ringwald     }
397021192e1SMilanka Ringwald     client->report_index = index;
3987e1e6e7dSMilanka Ringwald     return index;
3997e1e6e7dSMilanka Ringwald }
4007e1e6e7dSMilanka Ringwald 
4017e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
402021192e1SMilanka Ringwald     client->report_index++;
4037e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
40428da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
405e7bd2dbeSMilanka Ringwald         return true;
40670093cf5SMilanka Ringwald     }
4077e1e6e7dSMilanka Ringwald     return false;
4087e1e6e7dSMilanka Ringwald }
4097e1e6e7dSMilanka Ringwald 
4107e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
411021192e1SMilanka Ringwald     client->report_index = 0;
4127e1e6e7dSMilanka Ringwald 
4137e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
41428da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4157e1e6e7dSMilanka Ringwald         return true;
4167e1e6e7dSMilanka Ringwald     }
417e7bd2dbeSMilanka Ringwald     return false;
41870093cf5SMilanka Ringwald }
419ab116b1cSMilanka Ringwald 
420021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
421021192e1SMilanka Ringwald     uint8_t i;
422021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
423021192e1SMilanka Ringwald 
424021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
425021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
426fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
427fd39e93aSMilanka Ringwald 
428fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
429fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
430021192e1SMilanka Ringwald                 continue;
431021192e1SMilanka Ringwald             }
432fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
433fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
434fd39e93aSMilanka Ringwald                 continue;
435fd39e93aSMilanka Ringwald             }
436fd39e93aSMilanka Ringwald         }
437fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
438021192e1SMilanka Ringwald             index = i;
439021192e1SMilanka Ringwald         }
440021192e1SMilanka Ringwald     }
441021192e1SMilanka Ringwald     client->report_index = index;
442021192e1SMilanka Ringwald     return index;
443021192e1SMilanka Ringwald }
444021192e1SMilanka Ringwald 
445021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
446021192e1SMilanka Ringwald     client->report_index++;
447021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
448021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
449021192e1SMilanka Ringwald         return true;
450021192e1SMilanka Ringwald     }
451021192e1SMilanka Ringwald     return false;
452021192e1SMilanka Ringwald }
453021192e1SMilanka Ringwald 
454021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
455cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
456cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
457cd28e7b1SMilanka Ringwald #endif
458021192e1SMilanka Ringwald     client->report_index = 0;
459021192e1SMilanka Ringwald 
460021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
461021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
462021192e1SMilanka Ringwald         return true;
463021192e1SMilanka Ringwald     }
464021192e1SMilanka Ringwald     return false;
465021192e1SMilanka Ringwald }
466021192e1SMilanka Ringwald 
467cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
468cd28e7b1SMilanka Ringwald     client->report_index++;
469cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
470cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
471cd28e7b1SMilanka Ringwald         return true;
472cd28e7b1SMilanka Ringwald     }
473cd28e7b1SMilanka Ringwald     return false;
474cd28e7b1SMilanka Ringwald }
475cd28e7b1SMilanka Ringwald 
476cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
477cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
478cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
479cd28e7b1SMilanka Ringwald #endif
480cd28e7b1SMilanka Ringwald     client->report_index = 0;
481cd28e7b1SMilanka Ringwald 
482cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
483cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
484cd28e7b1SMilanka Ringwald         return true;
485cd28e7b1SMilanka Ringwald     }
486cd28e7b1SMilanka Ringwald     return false;
487cd28e7b1SMilanka Ringwald }
488cd28e7b1SMilanka Ringwald 
489cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
490cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
491cf26c8fbSMilanka Ringwald     if (!client){
492cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
493cf26c8fbSMilanka Ringwald         return NULL;
494cf26c8fbSMilanka Ringwald     }
495cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
496cf26c8fbSMilanka Ringwald     client->cid = cid;
497cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
498fc975d0eSMilanka Ringwald 
499cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
500cf26c8fbSMilanka Ringwald     return client;
501fc975d0eSMilanka Ringwald }
502fc975d0eSMilanka Ringwald 
503cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
504021192e1SMilanka Ringwald     // stop listening
505021192e1SMilanka Ringwald     uint8_t i;
506021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
507021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
508021192e1SMilanka Ringwald     }
509021192e1SMilanka Ringwald 
510da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
511cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
512cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
513fc975d0eSMilanka Ringwald }
514cf26c8fbSMilanka Ringwald 
515cf26c8fbSMilanka Ringwald 
516cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5176bcfb631SMilanka Ringwald     uint8_t event[8];
518cf26c8fbSMilanka Ringwald     int pos = 0;
519cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
520cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
521cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
522cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
523cf26c8fbSMilanka Ringwald     pos += 2;
524cf26c8fbSMilanka Ringwald     event[pos++] = status;
525fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
526cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
527cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
528cf26c8fbSMilanka Ringwald }
529cf26c8fbSMilanka Ringwald 
530*e5451bcdSMatthias Ringwald static void hids_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){
531*e5451bcdSMatthias Ringwald     uint8_t event[5];
532*e5451bcdSMatthias Ringwald     int pos = 0;
533*e5451bcdSMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
534*e5451bcdSMatthias Ringwald     event[pos++] = sizeof(event) - 2;
535*e5451bcdSMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_DISCONNECTED;
536*e5451bcdSMatthias Ringwald     little_endian_store_16(event, pos, cid);
537*e5451bcdSMatthias Ringwald     pos += 2;
538*e5451bcdSMatthias Ringwald     btstack_assert(pos == sizeof(event));
539*e5451bcdSMatthias Ringwald     (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
540*e5451bcdSMatthias Ringwald }
541*e5451bcdSMatthias Ringwald 
542cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
543cd28e7b1SMilanka Ringwald     uint8_t event[6];
544cd28e7b1SMilanka Ringwald     int pos = 0;
545cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
546cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
547cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
548cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
549cd28e7b1SMilanka Ringwald     pos += 2;
550cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
551cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
552cd28e7b1SMilanka Ringwald }
553cd28e7b1SMilanka Ringwald 
554e3bccb1cSMatthias Ringwald static uint16_t hids_client_setup_report_event(uint8_t subevent, hids_client_t *client, uint8_t report_index, uint8_t *buffer,
555e3bccb1cSMatthias Ringwald                                uint16_t report_len) {
556021192e1SMilanka Ringwald     uint16_t pos = 0;
557021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
558021192e1SMilanka Ringwald     pos++;  // skip len
559e3bccb1cSMatthias Ringwald     buffer[pos++] = subevent;
560021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
561021192e1SMilanka Ringwald     pos += 2;
562021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
563021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
564a2d3931bSMatthias Ringwald     little_endian_store_16(buffer, pos, report_len);
565021192e1SMilanka Ringwald     pos += 2;
566a2d3931bSMatthias Ringwald     buffer[1] = pos + report_len - 2;
567a2d3931bSMatthias Ringwald     return pos;
568a2d3931bSMatthias Ringwald }
56983d7ed1cSMilanka Ringwald 
570a2d3931bSMatthias 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){
571e3bccb1cSMatthias Ringwald     uint16_t pos = hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT, client, report_index, buffer,
572e3bccb1cSMatthias Ringwald                                                   report_len + 1);
573a2d3931bSMatthias Ringwald     buffer[pos] = client->reports[report_index].report_id;
574021192e1SMilanka Ringwald }
575021192e1SMilanka Ringwald 
576f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
577f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
578f4d3b82aSMilanka Ringwald 
579f4d3b82aSMilanka Ringwald     uint8_t event[11];
580f4d3b82aSMilanka Ringwald     int pos = 0;
581f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
582f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
583f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
584f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
585f4d3b82aSMilanka Ringwald     pos += 2;
586f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
587f4d3b82aSMilanka Ringwald 
588f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
589f4d3b82aSMilanka Ringwald     pos += 3;
590f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
591f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
592f4d3b82aSMilanka Ringwald 
593f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
594f4d3b82aSMilanka Ringwald }
595f4d3b82aSMilanka Ringwald 
596af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
597af2241c2SMilanka Ringwald     if (value_len != 1) return;
598af2241c2SMilanka Ringwald 
599af2241c2SMilanka Ringwald     uint8_t event[11];
600af2241c2SMilanka Ringwald     int pos = 0;
601af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
602af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
603af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
604af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
605af2241c2SMilanka Ringwald     pos += 2;
606af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
607af2241c2SMilanka Ringwald     event[pos++] = value[0];
608af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
609af2241c2SMilanka Ringwald }
610f4d3b82aSMilanka Ringwald 
61183d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
612021192e1SMilanka Ringwald     UNUSED(packet_type);
613021192e1SMilanka Ringwald     UNUSED(channel);
61483d7ed1cSMilanka Ringwald 
615021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
616021192e1SMilanka Ringwald 
617021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
61817229983SMatthias Ringwald     if (client == NULL) return;
619021192e1SMilanka Ringwald 
620021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
621021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
622021192e1SMilanka Ringwald         return;
623021192e1SMilanka Ringwald     }
624021192e1SMilanka Ringwald 
62583d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
626a2d3931bSMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
627a2d3931bSMatthias Ringwald                                                   gatt_event_notification_get_value_length(packet));
62883d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
629021192e1SMilanka Ringwald }
630021192e1SMilanka Ringwald 
63183d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
63283d7ed1cSMilanka Ringwald     UNUSED(packet_type);
63383d7ed1cSMilanka Ringwald     UNUSED(channel);
63483d7ed1cSMilanka Ringwald 
63583d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
63683d7ed1cSMilanka Ringwald 
63783d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
63817229983SMatthias Ringwald     if (client == NULL) return;
63983d7ed1cSMilanka Ringwald 
64083d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
64183d7ed1cSMilanka Ringwald         return;
64283d7ed1cSMilanka Ringwald     }
64383d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
64483d7ed1cSMilanka Ringwald 
64583d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
64683d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
64783d7ed1cSMilanka Ringwald         return;
64883d7ed1cSMilanka Ringwald     }
64983d7ed1cSMilanka Ringwald 
65083d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
65184d4ab66SMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
65284d4ab66SMatthias Ringwald                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
65383d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
65483d7ed1cSMilanka Ringwald }
655cf26c8fbSMilanka Ringwald 
656cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
657cf26c8fbSMilanka Ringwald     uint8_t att_status;
6586bcfb631SMilanka Ringwald     gatt_client_service_t service;
6596bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
660cf26c8fbSMilanka Ringwald 
661cf26c8fbSMilanka Ringwald     switch (client->state){
662cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
663556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
664556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
665556456ccSMilanka Ringwald #endif
666cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
66719146789SMilanka Ringwald 
66819146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
669cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
670cf26c8fbSMilanka Ringwald             UNUSED(att_status);
671cf26c8fbSMilanka Ringwald             break;
672cf26c8fbSMilanka Ringwald 
673cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
674556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
675556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
676556456ccSMilanka Ringwald #endif
6776bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6786bcfb631SMilanka Ringwald 
6796bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6806bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
68119146789SMilanka Ringwald 
68219146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6836bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6846bcfb631SMilanka Ringwald 
6856bcfb631SMilanka Ringwald             UNUSED(att_status);
6866bcfb631SMilanka Ringwald             break;
6876bcfb631SMilanka Ringwald 
68828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
689556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
690556456ccSMilanka 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);
691556456ccSMilanka Ringwald #endif
69228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
693556456ccSMilanka Ringwald 
69419146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
69519146789SMilanka 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);
696e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
697e7bd2dbeSMilanka Ringwald             break;
698e7bd2dbeSMilanka Ringwald 
699e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
700556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
701556456ccSMilanka 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);
702556456ccSMilanka Ringwald #endif
703e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
704e7bd2dbeSMilanka Ringwald 
7053322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
7063322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
7073322b222SMilanka Ringwald 
70819146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
709e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
710e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
711e7bd2dbeSMilanka Ringwald             break;
712e7bd2dbeSMilanka Ringwald 
71328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
714556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
71519146789SMilanka 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);
716556456ccSMilanka Ringwald #endif
71728da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
718e7bd2dbeSMilanka Ringwald 
71919146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
72019146789SMilanka 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);
721e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
722e7bd2dbeSMilanka Ringwald             break;
723e7bd2dbeSMilanka Ringwald 
7243322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
725556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
72619146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
727556456ccSMilanka Ringwald #endif
7283322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
729e7bd2dbeSMilanka Ringwald 
7303322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7313322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
73219146789SMilanka Ringwald 
73319146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7343322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
735e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
736e7bd2dbeSMilanka Ringwald             break;
737e7bd2dbeSMilanka Ringwald 
73828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
739556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
74083d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
741556456ccSMilanka Ringwald                 client->report_index,
74283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
74383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
744556456ccSMilanka Ringwald #endif
74528da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
746af2241c2SMilanka Ringwald             client->handle = 0;
747556456ccSMilanka Ringwald 
748556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
749556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
750556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
751556456ccSMilanka Ringwald 
75219146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
75370093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
75470093cf5SMilanka Ringwald             UNUSED(att_status);
75570093cf5SMilanka Ringwald             break;
75670093cf5SMilanka Ringwald 
75728da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
75828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
75970093cf5SMilanka Ringwald 
76019146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
761af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
762af2241c2SMilanka Ringwald             client->handle = 0;
76370093cf5SMilanka Ringwald             UNUSED(att_status);
76470093cf5SMilanka Ringwald             break;
76570093cf5SMilanka Ringwald 
766cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
767cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
768cd28e7b1SMilanka Ringwald             if (client->value > 0){
769cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
770cd28e7b1SMilanka Ringwald             } else {
771cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
772cd28e7b1SMilanka Ringwald             }
773cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
774cd28e7b1SMilanka Ringwald                 client->report_index,
775cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
776cd28e7b1SMilanka Ringwald #endif
777cd28e7b1SMilanka Ringwald 
778cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
779cd28e7b1SMilanka Ringwald 
780cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
781cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
782cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
783cd28e7b1SMilanka Ringwald 
784cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
785cd28e7b1SMilanka Ringwald 
786cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
787cd28e7b1SMilanka Ringwald 
788cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
789cd28e7b1SMilanka Ringwald                 switch(client->value){
790cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
791cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
792cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
793cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
794cd28e7b1SMilanka Ringwald                         break;
795cd28e7b1SMilanka Ringwald                     default:
796cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
797cd28e7b1SMilanka Ringwald                         break;
798cd28e7b1SMilanka Ringwald                 }
799cd28e7b1SMilanka Ringwald             } else {
800cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
801cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
802cd28e7b1SMilanka Ringwald                     break;
803cd28e7b1SMilanka Ringwald                 }
804cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
805cd28e7b1SMilanka Ringwald             }
806cd28e7b1SMilanka Ringwald             break;
807cd28e7b1SMilanka Ringwald 
808021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
809cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
810cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
811cd28e7b1SMilanka Ringwald                 client->report_index,
812cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
813cd28e7b1SMilanka Ringwald #endif
814021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
815021192e1SMilanka Ringwald 
816021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
817021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
818021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
819021192e1SMilanka Ringwald 
82019146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
821021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
822021192e1SMilanka Ringwald 
823cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
824cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
825cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
826cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
827cd28e7b1SMilanka Ringwald             } else {
828021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
829021192e1SMilanka Ringwald                     hids_run_for_client(client);
830021192e1SMilanka Ringwald                     break;
831021192e1SMilanka Ringwald                 }
832021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
833021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
834021192e1SMilanka Ringwald             }
835021192e1SMilanka Ringwald             break;
836021192e1SMilanka Ringwald 
83783d7ed1cSMilanka Ringwald 
8386d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
83983d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8406d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
84183d7ed1cSMilanka Ringwald                 client->report_index,
84283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
84383d7ed1cSMilanka Ringwald #endif
84483d7ed1cSMilanka Ringwald 
8456d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
84683d7ed1cSMilanka Ringwald 
8476d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8486d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
84984d4ab66SMatthias Ringwald                 &handle_gatt_client_event, client->con_handle,
85083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
85183d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
85283d7ed1cSMilanka Ringwald             UNUSED(att_status);
85383d7ed1cSMilanka Ringwald             break;
85483d7ed1cSMilanka Ringwald 
85583d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
85683d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
857f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
858f4d3b82aSMilanka Ringwald                 client->report_index,
85983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
86083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
86183d7ed1cSMilanka Ringwald #endif
86283d7ed1cSMilanka Ringwald 
86383d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
864f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
86583d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
86683d7ed1cSMilanka Ringwald                 &handle_report_event,
86783d7ed1cSMilanka Ringwald                 client->con_handle,
86883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
86983d7ed1cSMilanka Ringwald             UNUSED(att_status);
87083d7ed1cSMilanka Ringwald             break;
87183d7ed1cSMilanka Ringwald 
87283d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
87383d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
87483d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
87583d7ed1cSMilanka Ringwald 
876f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
87783d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
87883d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
87983d7ed1cSMilanka Ringwald                 client->con_handle,
88083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
88183d7ed1cSMilanka Ringwald 
88283d7ed1cSMilanka Ringwald             break;
88383d7ed1cSMilanka Ringwald #endif
884af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
885af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
88683d7ed1cSMilanka Ringwald 
887f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
888f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
889f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
890f4d3b82aSMilanka Ringwald                 client->con_handle,
891af2241c2SMilanka Ringwald                 client->handle);
892f4d3b82aSMilanka Ringwald             break;
8936d6f7efcSMilanka Ringwald 
894fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8956d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
8961fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
8971fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
8981fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
8991fa7278eSMatthias Ringwald             break;
9001fa7278eSMatthias Ringwald 
9011fa7278eSMatthias Ringwald         default:
9021fa7278eSMatthias Ringwald             break;
9031fa7278eSMatthias Ringwald     }
9041fa7278eSMatthias Ringwald }
9051fa7278eSMatthias Ringwald 
9061fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
9071fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
9081fa7278eSMatthias Ringwald     uint8_t att_status;
9091fa7278eSMatthias Ringwald     switch (client->state){
9101fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
9111fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
9121fa7278eSMatthias Ringwald                 client->con_handle,
9131fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
9141fa7278eSMatthias Ringwald 
9151fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9161fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
9171fa7278eSMatthias Ringwald #endif
9181fa7278eSMatthias Ringwald 
9191fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
9201fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
9211fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
9221fa7278eSMatthias Ringwald                     client->service_index++;
9231fa7278eSMatthias Ringwald                     hids_run_for_client(client);
9241fa7278eSMatthias Ringwald                     break;
9251fa7278eSMatthias Ringwald                 }
9261fa7278eSMatthias Ringwald             }
9271fa7278eSMatthias Ringwald 
9281fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
9291fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
9301fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9311fa7278eSMatthias Ringwald                 break;
9321fa7278eSMatthias Ringwald             }
9331fa7278eSMatthias Ringwald 
9341fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
9351fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
9361fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
9371fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9381fa7278eSMatthias Ringwald                 break;
9391fa7278eSMatthias Ringwald             }
9401fa7278eSMatthias Ringwald 
9411fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9421fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9431fa7278eSMatthias Ringwald             break;
9441fa7278eSMatthias Ringwald 
9451fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9461fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9471fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9481fa7278eSMatthias Ringwald #endif
9491fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9501fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9516d6f7efcSMilanka Ringwald             break;
9526d6f7efcSMilanka Ringwald 
9536bcfb631SMilanka Ringwald         default:
9546bcfb631SMilanka Ringwald             break;
9556bcfb631SMilanka Ringwald     }
9566bcfb631SMilanka Ringwald }
9576bcfb631SMilanka Ringwald 
958cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
959cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
960cf26c8fbSMilanka Ringwald     UNUSED(channel);
961cf26c8fbSMilanka Ringwald     UNUSED(size);
962cf26c8fbSMilanka Ringwald 
9636bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
964cf26c8fbSMilanka Ringwald     uint8_t att_status;
9656bcfb631SMilanka Ringwald     gatt_client_service_t service;
9666bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
96770093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
968ab116b1cSMilanka Ringwald 
969021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
970021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
971e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9723322b222SMilanka Ringwald     uint8_t i;
9733322b222SMilanka Ringwald     uint8_t report_index;
974da142a6fSMilanka Ringwald 
975f4d3b82aSMilanka Ringwald     const uint8_t * value;
976f4d3b82aSMilanka Ringwald     uint16_t value_len;
977cf26c8fbSMilanka Ringwald 
978cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
979cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
980cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
98117229983SMatthias Ringwald             if (client == NULL) break;
982cf26c8fbSMilanka Ringwald 
983021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
984021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9856bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
986021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
987021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9886bcfb631SMilanka Ringwald                 client->num_instances++;
989021192e1SMilanka Ringwald 
990708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
991708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
992708c69d2SMilanka Ringwald #endif
993021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
994021192e1SMilanka Ringwald             }  else {
995021192e1SMilanka 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);
996da142a6fSMilanka Ringwald             }
9976bcfb631SMilanka Ringwald             break;
9986bcfb631SMilanka Ringwald 
9996bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
10006bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
100117229983SMatthias Ringwald             if (client == NULL) break;
100217229983SMatthias Ringwald 
10036bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
100419146789SMilanka Ringwald 
100519146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
100619146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
100719146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
100819146789SMilanka Ringwald                     break;
100919146789SMilanka Ringwald                 }
101019146789SMilanka Ringwald             }
101119146789SMilanka Ringwald 
101219146789SMilanka Ringwald             switch (characteristic.uuid16){
101319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1014af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
101519146789SMilanka Ringwald                     break;
101619146789SMilanka Ringwald 
101719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
101819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
101919146789SMilanka Ringwald                     break;
102019146789SMilanka Ringwald 
102119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
102219146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
102319146789SMilanka Ringwald                     break;
102419146789SMilanka Ringwald 
102519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1026f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
102719146789SMilanka Ringwald                     break;
102819146789SMilanka Ringwald 
102919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
103019146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
103119146789SMilanka Ringwald                     break;
103219146789SMilanka Ringwald 
103319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
103419146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
103519146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
103619146789SMilanka Ringwald                     break;
103719146789SMilanka Ringwald 
103819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1039f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
104019146789SMilanka Ringwald                     break;
104119146789SMilanka Ringwald 
104219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1043f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
104419146789SMilanka Ringwald                     break;
104519146789SMilanka Ringwald 
104619146789SMilanka Ringwald                 default:
104719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
104819146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
104919146789SMilanka Ringwald #endif
105019146789SMilanka Ringwald                     return;
105119146789SMilanka Ringwald             }
105219146789SMilanka Ringwald 
105319146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
105419146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
105519146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
105619146789SMilanka Ringwald                 characteristic.start_handle,
105719146789SMilanka Ringwald                 characteristic.properties,
105819146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
105919146789SMilanka Ringwald                 client->service_index);
106019146789SMilanka Ringwald 
106119146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
106219146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
106319146789SMilanka Ringwald             }
106419146789SMilanka Ringwald             printf("\n");
106519146789SMilanka Ringwald #endif
1066cf26c8fbSMilanka Ringwald             break;
1067cf26c8fbSMilanka Ringwald 
10688cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1069e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10708cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
107117229983SMatthias Ringwald             if (client == NULL) break;
1072da142a6fSMilanka Ringwald 
1073f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1074f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10758cec2b74SMilanka Ringwald 
1076556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10778cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1078f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1079556456ccSMilanka Ringwald #endif
1080f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1081f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1082da142a6fSMilanka Ringwald                 if (!stored){
1083da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1084da142a6fSMilanka Ringwald                     break;
1085da142a6fSMilanka Ringwald                 }
1086da142a6fSMilanka Ringwald             }
1087e7bd2dbeSMilanka Ringwald             break;
1088e7bd2dbeSMilanka Ringwald 
108970093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
109070093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
109117229983SMatthias Ringwald             if (client == NULL) break;
109217229983SMatthias Ringwald 
109370093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
109470093cf5SMilanka Ringwald 
1095e7bd2dbeSMilanka Ringwald             switch (client->state) {
1096e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1097e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1098e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
109919146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
11003322b222SMilanka Ringwald 
1101556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1102556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1103556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1104556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1105556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1106556456ccSMilanka Ringwald                                 client->service_index, report_index);
1107556456ccSMilanka Ringwald                         }
1108556456ccSMilanka Ringwald #endif
1109e7bd2dbeSMilanka Ringwald                     }
1110e7bd2dbeSMilanka Ringwald                     break;
111128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
111270093cf5SMilanka Ringwald                     // setup for descriptor value query
111370093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1114af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1115556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1116556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1117556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1118556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
111919146789SMilanka Ringwald #endif
1120556456ccSMilanka Ringwald                     }
1121556456ccSMilanka Ringwald 
112219146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1123556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
112483d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1125556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1126556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1127556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1128556456ccSMilanka Ringwald                     }
1129556456ccSMilanka Ringwald #endif
113070093cf5SMilanka Ringwald                     break;
1131556456ccSMilanka Ringwald 
1132e7bd2dbeSMilanka Ringwald                 default:
1133e7bd2dbeSMilanka Ringwald                     break;
1134e7bd2dbeSMilanka Ringwald             }
1135e7bd2dbeSMilanka Ringwald             break;
113670093cf5SMilanka Ringwald 
113783d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
113883d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
113917229983SMatthias Ringwald             if (client == NULL) break;
114083d7ed1cSMilanka Ringwald 
1141f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1142f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1143f4d3b82aSMilanka Ringwald 
1144af2241c2SMilanka Ringwald 
1145f4d3b82aSMilanka Ringwald             switch (client->state){
1146f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1147f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
114883d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1149f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
115083d7ed1cSMilanka Ringwald                     break;
115183d7ed1cSMilanka Ringwald #endif
1152af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1153af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1154af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1155f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1156f4d3b82aSMilanka Ringwald                         break;
1157af2241c2SMilanka Ringwald                     }
1158af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1159af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1160af2241c2SMilanka Ringwald                         break;
1161af2241c2SMilanka Ringwald                     }
1162af2241c2SMilanka Ringwald                     break;
1163af2241c2SMilanka Ringwald                 }
1164f4d3b82aSMilanka Ringwald                 default:
1165f4d3b82aSMilanka Ringwald                     break;
1166f4d3b82aSMilanka Ringwald             }
1167f4d3b82aSMilanka Ringwald 
1168f4d3b82aSMilanka Ringwald             break;
116983d7ed1cSMilanka Ringwald 
117070093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
117170093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
117217229983SMatthias Ringwald             if (client == NULL) break;
1173e7bd2dbeSMilanka Ringwald 
1174e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1175e7bd2dbeSMilanka Ringwald                 break;
1176e7bd2dbeSMilanka Ringwald             }
11777e1e6e7dSMilanka Ringwald 
1178e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1179e7bd2dbeSMilanka Ringwald             switch (client->state) {
118028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1181e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
118219146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11833322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
118419146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1185556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
118619146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
118719146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1188556456ccSMilanka Ringwald #endif
11893322b222SMilanka Ringwald                     }
1190e7bd2dbeSMilanka Ringwald                     break;
1191e7bd2dbeSMilanka Ringwald 
119228da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
119319146789SMilanka Ringwald 
119419146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1195021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1196021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
119719146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
119883d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1199021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1200021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
120183d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
120219146789SMilanka Ringwald     #endif
120319146789SMilanka Ringwald                     }
1204e7bd2dbeSMilanka Ringwald                     break;
1205e7bd2dbeSMilanka Ringwald 
1206e7bd2dbeSMilanka Ringwald                 default:
1207e7bd2dbeSMilanka Ringwald                     break;
120870093cf5SMilanka Ringwald             }
120970093cf5SMilanka Ringwald             break;
121070093cf5SMilanka Ringwald 
1211cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1212cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
121317229983SMatthias Ringwald             if (client == NULL) break;
1214cf26c8fbSMilanka Ringwald 
1215cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1216cf26c8fbSMilanka Ringwald 
1217cf26c8fbSMilanka Ringwald             switch (client->state){
1218cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
12196bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12206bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12216bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12225fa2c39aSMilanka Ringwald                         return;
12236bcfb631SMilanka Ringwald                     }
12246bcfb631SMilanka Ringwald 
12256bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12266bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12276bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12285fa2c39aSMilanka Ringwald                         return;
12296bcfb631SMilanka Ringwald                     }
12306bcfb631SMilanka Ringwald 
12316bcfb631SMilanka Ringwald                     client->service_index = 0;
123219146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12336bcfb631SMilanka Ringwald                     break;
12346bcfb631SMilanka Ringwald 
12356bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
12366bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12376bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12386bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12395fa2c39aSMilanka Ringwald                         return;
12406bcfb631SMilanka Ringwald                     }
12416bcfb631SMilanka Ringwald 
124270093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
124370093cf5SMilanka Ringwald                         // discover characteristics of next service
124470093cf5SMilanka Ringwald                         client->service_index++;
124570093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1246021192e1SMilanka Ringwald                         break;
1247021192e1SMilanka Ringwald                     }
1248021192e1SMilanka Ringwald 
1249fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1250fd39e93aSMilanka Ringwald 
1251021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1252021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1253fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1254fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1255021192e1SMilanka Ringwald                             }
12567e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12577e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12587e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
125970093cf5SMilanka Ringwald                                 break;
126070093cf5SMilanka Ringwald                             }
126170093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
126270093cf5SMilanka Ringwald                             hids_finalize_client(client);
1263fd39e93aSMilanka Ringwald                             return;
1264fd39e93aSMilanka Ringwald 
1265fd39e93aSMilanka Ringwald                         default:
1266fd39e93aSMilanka Ringwald                             // set boot mode
1267fd39e93aSMilanka Ringwald                             client->service_index = 0;
1268fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1269fd39e93aSMilanka Ringwald                             break;
1270fd39e93aSMilanka Ringwald                     }
12716bcfb631SMilanka Ringwald                     break;
12726bcfb631SMilanka Ringwald 
12733322b222SMilanka Ringwald 
127428da36a6SMilanka Ringwald                 // HID descriptor found
127528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1276e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1277e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1278e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12795fa2c39aSMilanka Ringwald                         return;
1280e7bd2dbeSMilanka Ringwald                     }
1281e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1282e7bd2dbeSMilanka Ringwald                     break;
1283e7bd2dbeSMilanka Ringwald 
128428da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1285e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12863322b222SMilanka Ringwald                     // go for next report map
12873322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1288e7bd2dbeSMilanka Ringwald                         break;
1289e7bd2dbeSMilanka Ringwald                     }
1290e7bd2dbeSMilanka Ringwald 
12913322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12923322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1293e7bd2dbeSMilanka Ringwald                         break;
1294e7bd2dbeSMilanka Ringwald                     }
1295e7bd2dbeSMilanka Ringwald 
1296e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1297e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12987e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1299e7bd2dbeSMilanka Ringwald                         break;
1300e7bd2dbeSMilanka Ringwald                     }
1301e7bd2dbeSMilanka Ringwald 
1302e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1303e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13045fa2c39aSMilanka Ringwald                     return;
1305e7bd2dbeSMilanka Ringwald 
130628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1307e7bd2dbeSMilanka Ringwald                     // go for next map report
13083322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1309e7bd2dbeSMilanka Ringwald                         break;
1310e7bd2dbeSMilanka Ringwald                     }
1311e7bd2dbeSMilanka Ringwald 
13123322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
13133322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1314e7bd2dbeSMilanka Ringwald                     break;
1315e7bd2dbeSMilanka Ringwald 
13163322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1317e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1318e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13197e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1320e7bd2dbeSMilanka Ringwald                         break;
1321e7bd2dbeSMilanka Ringwald                     }
1322e7bd2dbeSMilanka Ringwald 
1323e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1324e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13255fa2c39aSMilanka Ringwald                     return;
1326e7bd2dbeSMilanka Ringwald 
132728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1328af2241c2SMilanka Ringwald                     if (client->handle != 0){
132928da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
133070093cf5SMilanka Ringwald                         break;
133170093cf5SMilanka Ringwald                     }
133270093cf5SMilanka Ringwald                     // go for next report
13337e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
133470093cf5SMilanka Ringwald                         break;
133570093cf5SMilanka Ringwald                     }
1336835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
133770093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
133870093cf5SMilanka Ringwald                     break;
133970093cf5SMilanka Ringwald 
134028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
134170093cf5SMilanka Ringwald                     // go for next report
13427e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
134370093cf5SMilanka Ringwald                         break;
134470093cf5SMilanka Ringwald                     }
1345021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1346021192e1SMilanka Ringwald                         break;
1347021192e1SMilanka Ringwald                     }
1348835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
134970093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
135070093cf5SMilanka Ringwald                     break;
135170093cf5SMilanka Ringwald 
1352021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1353021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13542901a9b7SMilanka Ringwald                         break;
13552901a9b7SMilanka Ringwald                     }
1356835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13576bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13586bcfb631SMilanka Ringwald                     break;
1359f4d3b82aSMilanka Ringwald 
1360cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1361cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1362cd28e7b1SMilanka Ringwald                         break;
1363cd28e7b1SMilanka Ringwald                     }
1364cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1365cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1366cd28e7b1SMilanka Ringwald                     break;
1367cd28e7b1SMilanka Ringwald 
1368835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1369835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1370835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1371835a13f1SMilanka Ringwald                     break;
1372835a13f1SMilanka Ringwald #endif
1373f4d3b82aSMilanka Ringwald 
1374af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1375f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1376f4d3b82aSMilanka Ringwald                     break;
1377f4d3b82aSMilanka Ringwald 
137884d4ab66SMatthias Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
137984d4ab66SMatthias Ringwald                     {
138084d4ab66SMatthias Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
138184d4ab66SMatthias Ringwald 
138284d4ab66SMatthias Ringwald                         // emit empty report to signal done
138384d4ab66SMatthias Ringwald                         uint8_t event[9];
1384e3bccb1cSMatthias Ringwald                         hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1385e3bccb1cSMatthias Ringwald                                                        client->report_index, event, 0);
138684d4ab66SMatthias Ringwald                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
138784d4ab66SMatthias Ringwald                     }
138884d4ab66SMatthias Ringwald                     break;
13896d6f7efcSMilanka Ringwald 
1390cf26c8fbSMilanka Ringwald                 default:
1391cf26c8fbSMilanka Ringwald                     break;
1392cf26c8fbSMilanka Ringwald             }
1393cf26c8fbSMilanka Ringwald             break;
1394cf26c8fbSMilanka Ringwald 
1395cf26c8fbSMilanka Ringwald         default:
1396cf26c8fbSMilanka Ringwald             break;
1397cf26c8fbSMilanka Ringwald     }
13986bcfb631SMilanka Ringwald 
13996bcfb631SMilanka Ringwald     if (client != NULL){
14006bcfb631SMilanka Ringwald         hids_run_for_client(client);
14016bcfb631SMilanka Ringwald     }
1402cf26c8fbSMilanka Ringwald }
1403cf26c8fbSMilanka Ringwald 
1404*e5451bcdSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1405*e5451bcdSMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
1406*e5451bcdSMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
1407*e5451bcdSMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
1408*e5451bcdSMatthias Ringwald 
1409*e5451bcdSMatthias Ringwald     hci_con_handle_t con_handle;
1410*e5451bcdSMatthias Ringwald     hids_client_t * client;
1411*e5451bcdSMatthias Ringwald 
1412*e5451bcdSMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
1413*e5451bcdSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
1414*e5451bcdSMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
1415*e5451bcdSMatthias Ringwald             client = hids_get_client_for_con_handle(con_handle);
1416*e5451bcdSMatthias Ringwald             if (client != NULL){
1417*e5451bcdSMatthias Ringwald                 // emit disconnected
1418*e5451bcdSMatthias Ringwald                 btstack_packet_handler_t packet_handler = client->client_handler;
1419*e5451bcdSMatthias Ringwald                 uint16_t cid = client->cid;
1420*e5451bcdSMatthias Ringwald                 hids_emit_disconnected(packet_handler, cid);
1421*e5451bcdSMatthias Ringwald                 // finalize
1422*e5451bcdSMatthias Ringwald                 hids_finalize_client(client);
1423*e5451bcdSMatthias Ringwald             }
1424*e5451bcdSMatthias Ringwald             break;
1425*e5451bcdSMatthias Ringwald         default:
1426*e5451bcdSMatthias Ringwald             break;
1427*e5451bcdSMatthias Ringwald     }
1428*e5451bcdSMatthias Ringwald }
1429*e5451bcdSMatthias Ringwald 
14306bcfb631SMilanka 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){
1431cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1432cf26c8fbSMilanka Ringwald 
1433cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1434cf26c8fbSMilanka Ringwald     if (client != NULL){
1435cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1436cf26c8fbSMilanka Ringwald     }
1437cf26c8fbSMilanka Ringwald 
1438cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1439cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1440cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1441cf26c8fbSMilanka Ringwald     }
1442cf26c8fbSMilanka Ringwald 
1443cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1444cf26c8fbSMilanka Ringwald     if (client == NULL) {
1445cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1446cf26c8fbSMilanka Ringwald     }
1447cf26c8fbSMilanka Ringwald 
14486bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1449cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1450cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1451cf26c8fbSMilanka Ringwald 
1452cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1453cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1454cf26c8fbSMilanka Ringwald }
1455cf26c8fbSMilanka Ringwald 
1456cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1457cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1458cf26c8fbSMilanka Ringwald     if (client == NULL){
1459cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1460cf26c8fbSMilanka Ringwald     }
1461cf26c8fbSMilanka Ringwald     // finalize connection
1462cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1463cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1464cf26c8fbSMilanka Ringwald }
1465cf26c8fbSMilanka Ringwald 
1466fd39e93aSMilanka 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){
14671624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14681624214bSMilanka Ringwald     if (client == NULL){
14691624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14701624214bSMilanka Ringwald     }
14711624214bSMilanka Ringwald 
14721624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14731624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14741624214bSMilanka Ringwald     }
14751624214bSMilanka Ringwald 
1476fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
147783d7ed1cSMilanka Ringwald 
14781624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14791624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14801624214bSMilanka Ringwald     }
14811624214bSMilanka Ringwald 
14821624214bSMilanka Ringwald     uint16_t mtu;
14831624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14841624214bSMilanka Ringwald 
14851624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14861624214bSMilanka Ringwald         return status;
14871624214bSMilanka Ringwald     }
14881624214bSMilanka Ringwald 
14891624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14901624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14911624214bSMilanka Ringwald     }
14921624214bSMilanka Ringwald 
14936d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1494021192e1SMilanka Ringwald     client->report_index = report_index;
14951624214bSMilanka Ringwald     client->report = report;
14961624214bSMilanka Ringwald     client->report_len = report_len;
14971624214bSMilanka Ringwald 
14981624214bSMilanka Ringwald     hids_run_for_client(client);
14991624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15001624214bSMilanka Ringwald }
15011624214bSMilanka Ringwald 
1502fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
150383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
150483d7ed1cSMilanka Ringwald     if (client == NULL){
150583d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
150683d7ed1cSMilanka Ringwald     }
150783d7ed1cSMilanka Ringwald 
150883d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
150983d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
151083d7ed1cSMilanka Ringwald     }
151183d7ed1cSMilanka Ringwald 
1512fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
151383d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
151483d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
151583d7ed1cSMilanka Ringwald     }
151683d7ed1cSMilanka Ringwald 
151783d7ed1cSMilanka Ringwald     client->report_index = report_index;
151883d7ed1cSMilanka Ringwald 
151983d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
152083d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
152183d7ed1cSMilanka Ringwald #else
152283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
152383d7ed1cSMilanka Ringwald #endif
152483d7ed1cSMilanka Ringwald     hids_run_for_client(client);
152583d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
152683d7ed1cSMilanka Ringwald }
152783d7ed1cSMilanka Ringwald 
152883d7ed1cSMilanka Ringwald 
1529f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1530f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1531f4d3b82aSMilanka Ringwald     if (client == NULL){
1532f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1533f4d3b82aSMilanka Ringwald     }
1534f4d3b82aSMilanka Ringwald 
1535f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1536f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1537f4d3b82aSMilanka Ringwald     }
1538f4d3b82aSMilanka Ringwald 
1539f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1540f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1541f4d3b82aSMilanka Ringwald     }
1542f4d3b82aSMilanka Ringwald 
1543f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1544af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1545af2241c2SMilanka Ringwald 
1546af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1547af2241c2SMilanka Ringwald     hids_run_for_client(client);
1548af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1549af2241c2SMilanka Ringwald }
1550af2241c2SMilanka Ringwald 
1551af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1552af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1553af2241c2SMilanka Ringwald     if (client == NULL){
1554af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1555af2241c2SMilanka Ringwald     }
1556af2241c2SMilanka Ringwald 
1557af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1558af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1559af2241c2SMilanka Ringwald     }
1560af2241c2SMilanka Ringwald 
1561af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1562af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1563af2241c2SMilanka Ringwald     }
1564af2241c2SMilanka Ringwald 
1565af2241c2SMilanka Ringwald     client->service_index = service_index;
1566af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1567af2241c2SMilanka Ringwald 
1568af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1569f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1570f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1571f4d3b82aSMilanka Ringwald }
1572f4d3b82aSMilanka Ringwald 
15730cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15746d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15756d6f7efcSMilanka Ringwald     if (client == NULL){
15766d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15776d6f7efcSMilanka Ringwald     }
15786d6f7efcSMilanka Ringwald 
15796d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15806d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15816d6f7efcSMilanka Ringwald     }
15826d6f7efcSMilanka Ringwald 
15836d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15846d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15856d6f7efcSMilanka Ringwald     }
15866d6f7efcSMilanka Ringwald 
15876d6f7efcSMilanka Ringwald     client->service_index = service_index;
15886d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15896d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15906d6f7efcSMilanka Ringwald 
15916d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15926d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15936d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15946d6f7efcSMilanka Ringwald }
15956d6f7efcSMilanka Ringwald 
15966d6f7efcSMilanka Ringwald 
15976d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15986d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15996d6f7efcSMilanka Ringwald     if (client == NULL){
16006d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
16016d6f7efcSMilanka Ringwald     }
16026d6f7efcSMilanka Ringwald 
16036d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
16046d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
16056d6f7efcSMilanka Ringwald     }
16066d6f7efcSMilanka Ringwald 
16076d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
16086d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
16096d6f7efcSMilanka Ringwald     }
16106d6f7efcSMilanka Ringwald 
16116d6f7efcSMilanka Ringwald     client->service_index = service_index;
16126d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
16136d6f7efcSMilanka Ringwald     client->value = value;
16146d6f7efcSMilanka Ringwald 
16156d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
16166d6f7efcSMilanka Ringwald     hids_run_for_client(client);
16176d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
16186d6f7efcSMilanka Ringwald }
16196d6f7efcSMilanka Ringwald 
16206d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
16216d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
16226d6f7efcSMilanka Ringwald }
16236d6f7efcSMilanka Ringwald 
16246d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
16256d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
16266d6f7efcSMilanka Ringwald }
16276d6f7efcSMilanka Ringwald 
16280cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1629cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1630cd28e7b1SMilanka Ringwald     if (client == NULL){
1631cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1632cd28e7b1SMilanka Ringwald     }
1633cd28e7b1SMilanka Ringwald 
1634cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1635cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1636cd28e7b1SMilanka Ringwald     }
1637cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1638cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1639cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1640cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1641cd28e7b1SMilanka Ringwald     }
1642cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1643cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1644cd28e7b1SMilanka Ringwald }
1645cd28e7b1SMilanka Ringwald 
16460cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1647cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1648cd28e7b1SMilanka Ringwald     if (client == NULL){
1649cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1650cd28e7b1SMilanka Ringwald     }
1651cd28e7b1SMilanka Ringwald 
1652cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1653cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1654cd28e7b1SMilanka Ringwald     }
1655cd28e7b1SMilanka Ringwald 
1656cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1657cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1658cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1659cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1660cd28e7b1SMilanka Ringwald     }
1661cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1662cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1663cd28e7b1SMilanka Ringwald }
16646d6f7efcSMilanka Ringwald 
1665021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1666021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1667021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1668*e5451bcdSMatthias Ringwald 
1669*e5451bcdSMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
1670*e5451bcdSMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
1671021192e1SMilanka Ringwald }
1672cf26c8fbSMilanka Ringwald 
1673cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1674