xref: /btstack/src/ble/gatt-service/hids_client.c (revision 84d4ab66f762da9e8c1543138b3accf3cb26d6c0)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/core.h"
53fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
54cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
55fc975d0eSMilanka Ringwald #include "btstack_debug.h"
56fc975d0eSMilanka Ringwald #include "btstack_event.h"
57fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
58fc975d0eSMilanka Ringwald #include "gap.h"
59fc975d0eSMilanka Ringwald 
6070093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6470093cf5SMilanka Ringwald 
65da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
66da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
67da142a6fSMilanka Ringwald 
68da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
69da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
70da142a6fSMilanka Ringwald 
71cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
721fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context);
73cf26c8fbSMilanka Ringwald 
74556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
75556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
76556456ccSMilanka Ringwald     switch (uuid){
77556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
78556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
79556456ccSMilanka Ringwald 
80556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
81556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
82556456ccSMilanka Ringwald 
83556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
84556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
85556456ccSMilanka Ringwald 
86556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
87556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
88556456ccSMilanka Ringwald 
89556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
90556456ccSMilanka Ringwald             return "REPORT";
91556456ccSMilanka Ringwald 
92556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
93556456ccSMilanka Ringwald             return "REPORT_MAP";
94556456ccSMilanka Ringwald 
95556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
96556456ccSMilanka Ringwald             return "HID_INFORMATION";
97556456ccSMilanka Ringwald 
98556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
99556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
100556456ccSMilanka Ringwald         default:
101556456ccSMilanka Ringwald             return "UKNOWN";
102556456ccSMilanka Ringwald     }
103556456ccSMilanka Ringwald }
104556456ccSMilanka Ringwald #endif
105556456ccSMilanka Ringwald 
106da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
107da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
109da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
110da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
111da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
112da142a6fSMilanka Ringwald         return client;
113da142a6fSMilanka Ringwald     }
114da142a6fSMilanka Ringwald     return NULL;
115da142a6fSMilanka Ringwald }
116da142a6fSMilanka Ringwald 
117da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
118da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
120da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
121da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
122da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
123da142a6fSMilanka Ringwald         return client;
124da142a6fSMilanka Ringwald     }
125da142a6fSMilanka Ringwald     return NULL;
126da142a6fSMilanka Ringwald }
127da142a6fSMilanka Ringwald 
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald // START Descriptor Storage Util
130da142a6fSMilanka Ringwald 
131a391be9aSMatthias Ringwald static uint16_t hids_client_descriptors_len(hids_client_t * client){
132a391be9aSMatthias Ringwald     uint16_t descriptors_len = 0;
133a391be9aSMatthias Ringwald     uint8_t service_index;
134a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
135a391be9aSMatthias Ringwald         descriptors_len += client->services[service_index].hid_descriptor_len;
136a391be9aSMatthias Ringwald     }
137a391be9aSMatthias Ringwald     return descriptors_len;
138a391be9aSMatthias Ringwald }
139a391be9aSMatthias Ringwald 
140da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
141da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
142da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
143da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
144da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
145da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
146da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
147a391be9aSMatthias Ringwald         free_space -= hids_client_descriptors_len(client);
148da142a6fSMilanka Ringwald     }
149da142a6fSMilanka Ringwald     return free_space;
150da142a6fSMilanka Ringwald }
151da142a6fSMilanka Ringwald 
152da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
153a391be9aSMatthias Ringwald     // reserve remaining space for this connection
154a391be9aSMatthias Ringwald     uint16_t available_space = hids_client_descriptor_storage_get_available_space();
155da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
156a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_max_len = available_space;
157a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
158da142a6fSMilanka Ringwald }
159da142a6fSMilanka Ringwald 
160da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
161a391be9aSMatthias Ringwald     // store single hid descriptor byte
162da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
163da142a6fSMilanka Ringwald 
164da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
165da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
166da142a6fSMilanka Ringwald     return true;
167da142a6fSMilanka Ringwald }
168da142a6fSMilanka Ringwald 
169da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
170a391be9aSMatthias Ringwald     uint8_t service_index;
171da142a6fSMilanka Ringwald 
172a391be9aSMatthias Ringwald     // calculate descriptors len
173a391be9aSMatthias Ringwald     uint16_t descriptors_len = hids_client_descriptors_len(client);
174da142a6fSMilanka Ringwald 
175a391be9aSMatthias Ringwald     if (descriptors_len > 0){
176a391be9aSMatthias Ringwald         // move higher descriptors down
177a391be9aSMatthias Ringwald         uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
178da142a6fSMilanka Ringwald         memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
179da142a6fSMilanka Ringwald                 &hids_client_descriptor_storage[next_offset],
180da142a6fSMilanka Ringwald                 hids_client_descriptor_storage_len - next_offset);
181da142a6fSMilanka Ringwald 
182a391be9aSMatthias Ringwald         // fix descriptor offset of higher descriptors
183da142a6fSMilanka Ringwald         btstack_linked_list_iterator_t it;
184da142a6fSMilanka Ringwald         btstack_linked_list_iterator_init(&it, &clients);
185da142a6fSMilanka Ringwald         while (btstack_linked_list_iterator_has_next(&it)){
186da142a6fSMilanka Ringwald             hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
187a391be9aSMatthias Ringwald             if (conn == client) continue;
188a391be9aSMatthias Ringwald             for (service_index = 0; service_index < client->num_instances; service_index++){
189a391be9aSMatthias Ringwald                 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
190a391be9aSMatthias Ringwald                     conn->services[service_index].hid_descriptor_offset -= descriptors_len;
191da142a6fSMilanka Ringwald                 }
192da142a6fSMilanka Ringwald             }
193da142a6fSMilanka Ringwald         }
194da142a6fSMilanka Ringwald     }
195da142a6fSMilanka Ringwald 
196a391be9aSMatthias Ringwald     // clear descriptors
197a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
198a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_len = 0;
199a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_offset = 0;
200a391be9aSMatthias Ringwald     }
201a391be9aSMatthias Ringwald }
202a391be9aSMatthias Ringwald 
203da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
204da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
205021192e1SMilanka Ringwald     if (client == NULL){
206021192e1SMilanka Ringwald         return NULL;
207021192e1SMilanka Ringwald     }
208021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
209da142a6fSMilanka Ringwald         return NULL;
210da142a6fSMilanka Ringwald     }
211da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
212da142a6fSMilanka Ringwald }
213da142a6fSMilanka Ringwald 
214da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
215da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
216021192e1SMilanka Ringwald     if (client == NULL){
217021192e1SMilanka Ringwald         return 0;
218021192e1SMilanka Ringwald     }
219021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
220da142a6fSMilanka Ringwald         return 0;
221da142a6fSMilanka Ringwald     }
222da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
223da142a6fSMilanka Ringwald }
224da142a6fSMilanka Ringwald 
225da142a6fSMilanka Ringwald // END Descriptor Storage Util
226da142a6fSMilanka Ringwald 
227cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
228cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
229cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
230cf26c8fbSMilanka Ringwald     } else {
231cf26c8fbSMilanka Ringwald         hids_cid_counter++;
232cf26c8fbSMilanka Ringwald     }
233cf26c8fbSMilanka Ringwald     return hids_cid_counter;
234fc975d0eSMilanka Ringwald }
235fc975d0eSMilanka Ringwald 
236ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
237ab116b1cSMilanka Ringwald     uint8_t i;
238a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
239ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
240ab116b1cSMilanka Ringwald             return i;
241ab116b1cSMilanka Ringwald         }
242ab116b1cSMilanka Ringwald     }
243ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
244ab116b1cSMilanka Ringwald }
245ab116b1cSMilanka Ringwald 
24619146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
24719146789SMilanka Ringwald     uint8_t i;
24819146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24919146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
25019146789SMilanka Ringwald             return i;
25119146789SMilanka Ringwald         }
25219146789SMilanka Ringwald     }
25319146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
25419146789SMilanka Ringwald }
25519146789SMilanka Ringwald 
25619146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
25719146789SMilanka Ringwald     uint8_t i;
25819146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
25919146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
26019146789SMilanka Ringwald             return true;
26119146789SMilanka Ringwald         }
26219146789SMilanka Ringwald     }
26319146789SMilanka Ringwald     return false;
26419146789SMilanka Ringwald }
26519146789SMilanka Ringwald 
266fd39e93aSMilanka Ringwald static uint8_t find_report_index_for_report_id_and_report_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
26783d7ed1cSMilanka Ringwald     uint8_t i;
268835a13f1SMilanka Ringwald 
26983d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
270fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
271fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
272fd39e93aSMilanka Ringwald 
273fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
27483d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
27583d7ed1cSMilanka Ringwald                 continue;
27683d7ed1cSMilanka Ringwald             }
277fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
27883d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
27983d7ed1cSMilanka Ringwald                 continue;
28083d7ed1cSMilanka Ringwald             }
281fd39e93aSMilanka Ringwald         }
282fd39e93aSMilanka Ringwald 
283fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
28483d7ed1cSMilanka Ringwald             return i;
28583d7ed1cSMilanka Ringwald         }
28683d7ed1cSMilanka Ringwald     }
28783d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
28883d7ed1cSMilanka Ringwald }
28919146789SMilanka Ringwald 
290021192e1SMilanka Ringwald static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type, bool boot_report){
291ab116b1cSMilanka Ringwald 
29219146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
293ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2943322b222SMilanka Ringwald         return report_index;
295ab116b1cSMilanka Ringwald     }
29628da36a6SMilanka Ringwald     report_index = client->num_reports;
297ab116b1cSMilanka Ringwald 
29828da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
29928da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
30028da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
30128da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
302ab116b1cSMilanka Ringwald 
30328da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
30428da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
30528da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
306021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
30770093cf5SMilanka Ringwald 
308021192e1SMilanka Ringwald         log_info("add index %d, id %d, type %d, value handle 0x%02x, properties 0x%02x", report_index, report_id, report_type, characteristic->value_handle, characteristic->properties);
309ab116b1cSMilanka Ringwald         client->num_reports++;
3103322b222SMilanka Ringwald         return report_index;
311ab116b1cSMilanka Ringwald     } else {
312ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3133322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
314ab116b1cSMilanka Ringwald     }
315ab116b1cSMilanka Ringwald }
316ab116b1cSMilanka Ringwald 
31719146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
318556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
319556456ccSMilanka Ringwald 
320556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
32119146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
322556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
32319146789SMilanka Ringwald 
324556456ccSMilanka Ringwald         client->num_external_reports++;
32519146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
326556456ccSMilanka Ringwald         return report_index;
327556456ccSMilanka Ringwald     } else {
328556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
329556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
33070093cf5SMilanka Ringwald     }
331556456ccSMilanka Ringwald }
332556456ccSMilanka Ringwald 
3337e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3347e1e6e7dSMilanka Ringwald     uint8_t i;
3353322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3363322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
337556456ccSMilanka Ringwald             return i;
3383322b222SMilanka Ringwald         }
3393322b222SMilanka Ringwald     }
340556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
341556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3423322b222SMilanka Ringwald }
3433322b222SMilanka Ringwald 
3443322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3453322b222SMilanka Ringwald     client->service_index++;
3463322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
347556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3483322b222SMilanka Ringwald         return true;
3493322b222SMilanka Ringwald     }
3503322b222SMilanka Ringwald     return false;
3513322b222SMilanka Ringwald }
3523322b222SMilanka Ringwald 
3533322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3543322b222SMilanka Ringwald     client->service_index = 0;
3553322b222SMilanka Ringwald 
3563322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3573322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3583322b222SMilanka Ringwald         return true;
3593322b222SMilanka Ringwald     }
3603322b222SMilanka Ringwald     return false;
3613322b222SMilanka Ringwald }
3623322b222SMilanka Ringwald 
3633322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
364021192e1SMilanka Ringwald     client->report_index++;
36519146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3663322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
367e7bd2dbeSMilanka Ringwald         return true;
368e7bd2dbeSMilanka Ringwald     }
369e7bd2dbeSMilanka Ringwald     return false;
370e7bd2dbeSMilanka Ringwald }
371e7bd2dbeSMilanka Ringwald 
3723322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
373021192e1SMilanka Ringwald     client->report_index = 0;
37419146789SMilanka Ringwald     if (client->num_external_reports > 0){
3753322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3767e1e6e7dSMilanka Ringwald         return true;
3777e1e6e7dSMilanka Ringwald     }
3787e1e6e7dSMilanka Ringwald     return false;
3797e1e6e7dSMilanka Ringwald }
3807e1e6e7dSMilanka Ringwald 
3817e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3827e1e6e7dSMilanka Ringwald     uint8_t i;
3837e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
384fd39e93aSMilanka Ringwald 
385021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
38670093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
387fd39e93aSMilanka Ringwald         if (!report.boot_report){
38870093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3897e1e6e7dSMilanka Ringwald                 index = i;
390021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3917e1e6e7dSMilanka Ringwald                 break;
39270093cf5SMilanka Ringwald             }
393fd39e93aSMilanka Ringwald         }
394fd39e93aSMilanka Ringwald     }
395021192e1SMilanka Ringwald     client->report_index = index;
3967e1e6e7dSMilanka Ringwald     return index;
3977e1e6e7dSMilanka Ringwald }
3987e1e6e7dSMilanka Ringwald 
3997e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
400021192e1SMilanka Ringwald     client->report_index++;
4017e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
40228da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
403e7bd2dbeSMilanka Ringwald         return true;
40470093cf5SMilanka Ringwald     }
4057e1e6e7dSMilanka Ringwald     return false;
4067e1e6e7dSMilanka Ringwald }
4077e1e6e7dSMilanka Ringwald 
4087e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
409021192e1SMilanka Ringwald     client->report_index = 0;
4107e1e6e7dSMilanka Ringwald 
4117e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
41228da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4137e1e6e7dSMilanka Ringwald         return true;
4147e1e6e7dSMilanka Ringwald     }
415e7bd2dbeSMilanka Ringwald     return false;
41670093cf5SMilanka Ringwald }
417ab116b1cSMilanka Ringwald 
418021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
419021192e1SMilanka Ringwald     uint8_t i;
420021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
421021192e1SMilanka Ringwald 
422021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
423021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
424fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
425fd39e93aSMilanka Ringwald 
426fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
427fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
428021192e1SMilanka Ringwald                 continue;
429021192e1SMilanka Ringwald             }
430fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
431fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
432fd39e93aSMilanka Ringwald                 continue;
433fd39e93aSMilanka Ringwald             }
434fd39e93aSMilanka Ringwald         }
435fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
436021192e1SMilanka Ringwald             index = i;
437021192e1SMilanka Ringwald         }
438021192e1SMilanka Ringwald     }
439021192e1SMilanka Ringwald     client->report_index = index;
440021192e1SMilanka Ringwald     return index;
441021192e1SMilanka Ringwald }
442021192e1SMilanka Ringwald 
443021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
444021192e1SMilanka Ringwald     client->report_index++;
445021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
446021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
447021192e1SMilanka Ringwald         return true;
448021192e1SMilanka Ringwald     }
449021192e1SMilanka Ringwald     return false;
450021192e1SMilanka Ringwald }
451021192e1SMilanka Ringwald 
452021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
453cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
454cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
455cd28e7b1SMilanka Ringwald #endif
456021192e1SMilanka Ringwald     client->report_index = 0;
457021192e1SMilanka Ringwald 
458021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
459021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
460021192e1SMilanka Ringwald         return true;
461021192e1SMilanka Ringwald     }
462021192e1SMilanka Ringwald     return false;
463021192e1SMilanka Ringwald }
464021192e1SMilanka Ringwald 
465cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
466cd28e7b1SMilanka Ringwald     client->report_index++;
467cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
468cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
469cd28e7b1SMilanka Ringwald         return true;
470cd28e7b1SMilanka Ringwald     }
471cd28e7b1SMilanka Ringwald     return false;
472cd28e7b1SMilanka Ringwald }
473cd28e7b1SMilanka Ringwald 
474cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
475cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
476cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
477cd28e7b1SMilanka Ringwald #endif
478cd28e7b1SMilanka Ringwald     client->report_index = 0;
479cd28e7b1SMilanka Ringwald 
480cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
481cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
482cd28e7b1SMilanka Ringwald         return true;
483cd28e7b1SMilanka Ringwald     }
484cd28e7b1SMilanka Ringwald     return false;
485cd28e7b1SMilanka Ringwald }
486cd28e7b1SMilanka Ringwald 
487cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
488cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
489cf26c8fbSMilanka Ringwald     if (!client){
490cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
491cf26c8fbSMilanka Ringwald         return NULL;
492cf26c8fbSMilanka Ringwald     }
493cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
494cf26c8fbSMilanka Ringwald     client->cid = cid;
495cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
496fc975d0eSMilanka Ringwald 
497cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
498cf26c8fbSMilanka Ringwald     return client;
499fc975d0eSMilanka Ringwald }
500fc975d0eSMilanka Ringwald 
501cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
502021192e1SMilanka Ringwald     // stop listening
503021192e1SMilanka Ringwald     uint8_t i;
504021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
505021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
506021192e1SMilanka Ringwald     }
507021192e1SMilanka Ringwald 
508da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
509cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
510cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
511fc975d0eSMilanka Ringwald }
512cf26c8fbSMilanka Ringwald 
513cf26c8fbSMilanka Ringwald 
514cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5156bcfb631SMilanka Ringwald     uint8_t event[8];
516cf26c8fbSMilanka Ringwald     int pos = 0;
517cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
518cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
519cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
520cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
521cf26c8fbSMilanka Ringwald     pos += 2;
522cf26c8fbSMilanka Ringwald     event[pos++] = status;
523fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
524cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
525cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
526cf26c8fbSMilanka Ringwald }
527cf26c8fbSMilanka Ringwald 
528cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
529cd28e7b1SMilanka Ringwald     uint8_t event[6];
530cd28e7b1SMilanka Ringwald     int pos = 0;
531cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
532cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
533cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
534cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
535cd28e7b1SMilanka Ringwald     pos += 2;
536cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
537cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
538cd28e7b1SMilanka Ringwald }
539cd28e7b1SMilanka Ringwald 
540a2d3931bSMatthias Ringwald static uint16_t hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
541021192e1SMilanka Ringwald     uint16_t pos = 0;
542021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
543021192e1SMilanka Ringwald     pos++;  // skip len
544021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
545021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
546021192e1SMilanka Ringwald     pos += 2;
547021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
548021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
549a2d3931bSMatthias Ringwald     little_endian_store_16(buffer, pos, report_len);
550021192e1SMilanka Ringwald     pos += 2;
551a2d3931bSMatthias Ringwald     buffer[1] = pos + report_len - 2;
552a2d3931bSMatthias Ringwald     return pos;
553a2d3931bSMatthias Ringwald }
55483d7ed1cSMilanka Ringwald 
555a2d3931bSMatthias 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){
556a2d3931bSMatthias Ringwald     uint16_t pos = hids_client_setup_report_event(client, report_index, buffer, report_len + 1);
557a2d3931bSMatthias Ringwald     buffer[pos] = client->reports[report_index].report_id;
558021192e1SMilanka Ringwald }
559021192e1SMilanka Ringwald 
560f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
561f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
562f4d3b82aSMilanka Ringwald 
563f4d3b82aSMilanka Ringwald     uint8_t event[11];
564f4d3b82aSMilanka Ringwald     int pos = 0;
565f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
566f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
567f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
568f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
569f4d3b82aSMilanka Ringwald     pos += 2;
570f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
571f4d3b82aSMilanka Ringwald 
572f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
573f4d3b82aSMilanka Ringwald     pos += 3;
574f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
575f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
576f4d3b82aSMilanka Ringwald 
577f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
578f4d3b82aSMilanka Ringwald }
579f4d3b82aSMilanka Ringwald 
580af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
581af2241c2SMilanka Ringwald     if (value_len != 1) return;
582af2241c2SMilanka Ringwald 
583af2241c2SMilanka Ringwald     uint8_t event[11];
584af2241c2SMilanka Ringwald     int pos = 0;
585af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
586af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
587af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
588af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
589af2241c2SMilanka Ringwald     pos += 2;
590af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
591af2241c2SMilanka Ringwald     event[pos++] = value[0];
592af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
593af2241c2SMilanka Ringwald }
594f4d3b82aSMilanka Ringwald 
59583d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
596021192e1SMilanka Ringwald     UNUSED(packet_type);
597021192e1SMilanka Ringwald     UNUSED(channel);
59883d7ed1cSMilanka Ringwald 
599021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
600021192e1SMilanka Ringwald 
601021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
60217229983SMatthias Ringwald     if (client == NULL) return;
603021192e1SMilanka Ringwald 
604021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
605021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
606021192e1SMilanka Ringwald         return;
607021192e1SMilanka Ringwald     }
608021192e1SMilanka Ringwald 
60983d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
610a2d3931bSMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
611a2d3931bSMatthias Ringwald                                                   gatt_event_notification_get_value_length(packet));
61283d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
613021192e1SMilanka Ringwald }
614021192e1SMilanka Ringwald 
61583d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
61683d7ed1cSMilanka Ringwald     UNUSED(packet_type);
61783d7ed1cSMilanka Ringwald     UNUSED(channel);
61883d7ed1cSMilanka Ringwald 
61983d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
62083d7ed1cSMilanka Ringwald 
62183d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
62217229983SMatthias Ringwald     if (client == NULL) return;
62383d7ed1cSMilanka Ringwald 
62483d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
62583d7ed1cSMilanka Ringwald         return;
62683d7ed1cSMilanka Ringwald     }
62783d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
62883d7ed1cSMilanka Ringwald 
62983d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
63083d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
63183d7ed1cSMilanka Ringwald         return;
63283d7ed1cSMilanka Ringwald     }
63383d7ed1cSMilanka Ringwald 
63483d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
635*84d4ab66SMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
636*84d4ab66SMatthias Ringwald                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
63783d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
63883d7ed1cSMilanka Ringwald }
639cf26c8fbSMilanka Ringwald 
640cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
641cf26c8fbSMilanka Ringwald     uint8_t att_status;
6426bcfb631SMilanka Ringwald     gatt_client_service_t service;
6436bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
644cf26c8fbSMilanka Ringwald 
645cf26c8fbSMilanka Ringwald     switch (client->state){
646cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
647556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
648556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
649556456ccSMilanka Ringwald #endif
650cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
65119146789SMilanka Ringwald 
65219146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
653cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
654cf26c8fbSMilanka Ringwald             UNUSED(att_status);
655cf26c8fbSMilanka Ringwald             break;
656cf26c8fbSMilanka Ringwald 
657cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
658556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
659556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
660556456ccSMilanka Ringwald #endif
6616bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6626bcfb631SMilanka Ringwald 
6636bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6646bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
66519146789SMilanka Ringwald 
66619146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6676bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6686bcfb631SMilanka Ringwald 
6696bcfb631SMilanka Ringwald             UNUSED(att_status);
6706bcfb631SMilanka Ringwald             break;
6716bcfb631SMilanka Ringwald 
67228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
673556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
674556456ccSMilanka 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);
675556456ccSMilanka Ringwald #endif
67628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
677556456ccSMilanka Ringwald 
67819146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
67919146789SMilanka 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);
680e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
681e7bd2dbeSMilanka Ringwald             break;
682e7bd2dbeSMilanka Ringwald 
683e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
684556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
685556456ccSMilanka 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);
686556456ccSMilanka Ringwald #endif
687e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
688e7bd2dbeSMilanka Ringwald 
6893322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6903322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6913322b222SMilanka Ringwald 
69219146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
693e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
694e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
695e7bd2dbeSMilanka Ringwald             break;
696e7bd2dbeSMilanka Ringwald 
69728da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
698556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
69919146789SMilanka 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);
700556456ccSMilanka Ringwald #endif
70128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
702e7bd2dbeSMilanka Ringwald 
70319146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
70419146789SMilanka 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);
705e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
706e7bd2dbeSMilanka Ringwald             break;
707e7bd2dbeSMilanka Ringwald 
7083322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
709556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
71019146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
711556456ccSMilanka Ringwald #endif
7123322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
713e7bd2dbeSMilanka Ringwald 
7143322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7153322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
71619146789SMilanka Ringwald 
71719146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7183322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
719e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
720e7bd2dbeSMilanka Ringwald             break;
721e7bd2dbeSMilanka Ringwald 
72228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
723556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
72483d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
725556456ccSMilanka Ringwald                 client->report_index,
72683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
72783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
728556456ccSMilanka Ringwald #endif
72928da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
730af2241c2SMilanka Ringwald             client->handle = 0;
731556456ccSMilanka Ringwald 
732556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
733556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
734556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
735556456ccSMilanka Ringwald 
73619146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
73770093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
73870093cf5SMilanka Ringwald             UNUSED(att_status);
73970093cf5SMilanka Ringwald             break;
74070093cf5SMilanka Ringwald 
74128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
74228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
74370093cf5SMilanka Ringwald 
74419146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
745af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
746af2241c2SMilanka Ringwald             client->handle = 0;
74770093cf5SMilanka Ringwald             UNUSED(att_status);
74870093cf5SMilanka Ringwald             break;
74970093cf5SMilanka Ringwald 
750cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
751cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
752cd28e7b1SMilanka Ringwald             if (client->value > 0){
753cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
754cd28e7b1SMilanka Ringwald             } else {
755cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
756cd28e7b1SMilanka Ringwald             }
757cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
758cd28e7b1SMilanka Ringwald                 client->report_index,
759cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
760cd28e7b1SMilanka Ringwald #endif
761cd28e7b1SMilanka Ringwald 
762cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
763cd28e7b1SMilanka Ringwald 
764cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
765cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
766cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
767cd28e7b1SMilanka Ringwald 
768cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
769cd28e7b1SMilanka Ringwald 
770cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
771cd28e7b1SMilanka Ringwald 
772cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
773cd28e7b1SMilanka Ringwald                 switch(client->value){
774cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
775cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
776cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
777cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
778cd28e7b1SMilanka Ringwald                         break;
779cd28e7b1SMilanka Ringwald                     default:
780cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
781cd28e7b1SMilanka Ringwald                         break;
782cd28e7b1SMilanka Ringwald                 }
783cd28e7b1SMilanka Ringwald             } else {
784cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
785cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
786cd28e7b1SMilanka Ringwald                     break;
787cd28e7b1SMilanka Ringwald                 }
788cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
789cd28e7b1SMilanka Ringwald             }
790cd28e7b1SMilanka Ringwald             break;
791cd28e7b1SMilanka Ringwald 
792021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
793cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
794cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
795cd28e7b1SMilanka Ringwald                 client->report_index,
796cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
797cd28e7b1SMilanka Ringwald #endif
798021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
799021192e1SMilanka Ringwald 
800021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
801021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
802021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
803021192e1SMilanka Ringwald 
80419146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
805021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
806021192e1SMilanka Ringwald 
807cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
808cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
809cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
810cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
811cd28e7b1SMilanka Ringwald             } else {
812021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
813021192e1SMilanka Ringwald                     hids_run_for_client(client);
814021192e1SMilanka Ringwald                     break;
815021192e1SMilanka Ringwald                 }
816021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
817021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
818021192e1SMilanka Ringwald             }
819021192e1SMilanka Ringwald             break;
820021192e1SMilanka Ringwald 
82183d7ed1cSMilanka Ringwald 
8226d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
82383d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8246d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
82583d7ed1cSMilanka Ringwald                 client->report_index,
82683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82783d7ed1cSMilanka Ringwald #endif
82883d7ed1cSMilanka Ringwald 
8296d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
83083d7ed1cSMilanka Ringwald 
8316d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8326d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
833*84d4ab66SMatthias Ringwald                 &handle_gatt_client_event, client->con_handle,
83483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
83583d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
83683d7ed1cSMilanka Ringwald             UNUSED(att_status);
83783d7ed1cSMilanka Ringwald             break;
83883d7ed1cSMilanka Ringwald 
83983d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
84083d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
841f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
842f4d3b82aSMilanka Ringwald                 client->report_index,
84383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
84483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
84583d7ed1cSMilanka Ringwald #endif
84683d7ed1cSMilanka Ringwald 
84783d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
848f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
84983d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
85083d7ed1cSMilanka Ringwald                 &handle_report_event,
85183d7ed1cSMilanka Ringwald                 client->con_handle,
85283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
85383d7ed1cSMilanka Ringwald             UNUSED(att_status);
85483d7ed1cSMilanka Ringwald             break;
85583d7ed1cSMilanka Ringwald 
85683d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
85783d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
85883d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
85983d7ed1cSMilanka Ringwald 
860f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
86183d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
86283d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
86383d7ed1cSMilanka Ringwald                 client->con_handle,
86483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
86583d7ed1cSMilanka Ringwald 
86683d7ed1cSMilanka Ringwald             break;
86783d7ed1cSMilanka Ringwald #endif
868af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
869af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
87083d7ed1cSMilanka Ringwald 
871f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
872f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
873f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
874f4d3b82aSMilanka Ringwald                 client->con_handle,
875af2241c2SMilanka Ringwald                 client->handle);
876f4d3b82aSMilanka Ringwald             break;
8776d6f7efcSMilanka Ringwald 
878fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8796d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
8801fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
8811fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
8821fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
8831fa7278eSMatthias Ringwald             break;
8841fa7278eSMatthias Ringwald 
8851fa7278eSMatthias Ringwald         default:
8861fa7278eSMatthias Ringwald             break;
8871fa7278eSMatthias Ringwald     }
8881fa7278eSMatthias Ringwald }
8891fa7278eSMatthias Ringwald 
8901fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
8911fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
8921fa7278eSMatthias Ringwald     uint8_t att_status;
8931fa7278eSMatthias Ringwald     switch (client->state){
8941fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8951fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
8961fa7278eSMatthias Ringwald                 client->con_handle,
8971fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
8981fa7278eSMatthias Ringwald 
8991fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9001fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
9011fa7278eSMatthias Ringwald #endif
9021fa7278eSMatthias Ringwald 
9031fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
9041fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
9051fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
9061fa7278eSMatthias Ringwald                     client->service_index++;
9071fa7278eSMatthias Ringwald                     hids_run_for_client(client);
9081fa7278eSMatthias Ringwald                     break;
9091fa7278eSMatthias Ringwald                 }
9101fa7278eSMatthias Ringwald             }
9111fa7278eSMatthias Ringwald 
9121fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
9131fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
9141fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9151fa7278eSMatthias Ringwald                 break;
9161fa7278eSMatthias Ringwald             }
9171fa7278eSMatthias Ringwald 
9181fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
9191fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
9201fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
9211fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9221fa7278eSMatthias Ringwald                 break;
9231fa7278eSMatthias Ringwald             }
9241fa7278eSMatthias Ringwald 
9251fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9261fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9271fa7278eSMatthias Ringwald             break;
9281fa7278eSMatthias Ringwald 
9291fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9301fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9311fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9321fa7278eSMatthias Ringwald #endif
9331fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9341fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9356d6f7efcSMilanka Ringwald             break;
9366d6f7efcSMilanka Ringwald 
9376bcfb631SMilanka Ringwald         default:
9386bcfb631SMilanka Ringwald             break;
9396bcfb631SMilanka Ringwald     }
9406bcfb631SMilanka Ringwald }
9416bcfb631SMilanka Ringwald 
942cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
943cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
944cf26c8fbSMilanka Ringwald     UNUSED(channel);
945cf26c8fbSMilanka Ringwald     UNUSED(size);
946cf26c8fbSMilanka Ringwald 
9476bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
948cf26c8fbSMilanka Ringwald     uint8_t att_status;
9496bcfb631SMilanka Ringwald     gatt_client_service_t service;
9506bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
95170093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
952ab116b1cSMilanka Ringwald 
953021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
954021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
955e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9563322b222SMilanka Ringwald     uint8_t i;
9573322b222SMilanka Ringwald     uint8_t report_index;
958da142a6fSMilanka Ringwald 
959f4d3b82aSMilanka Ringwald     const uint8_t * value;
960f4d3b82aSMilanka Ringwald     uint16_t value_len;
961cf26c8fbSMilanka Ringwald 
962cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
963cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
964cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
96517229983SMatthias Ringwald             if (client == NULL) break;
966cf26c8fbSMilanka Ringwald 
967021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
968021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9696bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
970021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
971021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9726bcfb631SMilanka Ringwald                 client->num_instances++;
973021192e1SMilanka Ringwald 
974708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
975708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
976708c69d2SMilanka Ringwald #endif
977021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
978021192e1SMilanka Ringwald             }  else {
979021192e1SMilanka 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);
980da142a6fSMilanka Ringwald             }
9816bcfb631SMilanka Ringwald             break;
9826bcfb631SMilanka Ringwald 
9836bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9846bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
98517229983SMatthias Ringwald             if (client == NULL) break;
98617229983SMatthias Ringwald 
9876bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
98819146789SMilanka Ringwald 
98919146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
99019146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
99119146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
99219146789SMilanka Ringwald                     break;
99319146789SMilanka Ringwald                 }
99419146789SMilanka Ringwald             }
99519146789SMilanka Ringwald 
99619146789SMilanka Ringwald             switch (characteristic.uuid16){
99719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
998af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
99919146789SMilanka Ringwald                     break;
100019146789SMilanka Ringwald 
100119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
100219146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
100319146789SMilanka Ringwald                     break;
100419146789SMilanka Ringwald 
100519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
100619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
100719146789SMilanka Ringwald                     break;
100819146789SMilanka Ringwald 
100919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1010f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
101119146789SMilanka Ringwald                     break;
101219146789SMilanka Ringwald 
101319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
101419146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
101519146789SMilanka Ringwald                     break;
101619146789SMilanka Ringwald 
101719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
101819146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
101919146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
102019146789SMilanka Ringwald                     break;
102119146789SMilanka Ringwald 
102219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1023f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
102419146789SMilanka Ringwald                     break;
102519146789SMilanka Ringwald 
102619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1027f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
102819146789SMilanka Ringwald                     break;
102919146789SMilanka Ringwald 
103019146789SMilanka Ringwald                 default:
103119146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103219146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
103319146789SMilanka Ringwald #endif
103419146789SMilanka Ringwald                     return;
103519146789SMilanka Ringwald             }
103619146789SMilanka Ringwald 
103719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103819146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
103919146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
104019146789SMilanka Ringwald                 characteristic.start_handle,
104119146789SMilanka Ringwald                 characteristic.properties,
104219146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
104319146789SMilanka Ringwald                 client->service_index);
104419146789SMilanka Ringwald 
104519146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
104619146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
104719146789SMilanka Ringwald             }
104819146789SMilanka Ringwald             printf("\n");
104919146789SMilanka Ringwald #endif
1050cf26c8fbSMilanka Ringwald             break;
1051cf26c8fbSMilanka Ringwald 
10528cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1053e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10548cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
105517229983SMatthias Ringwald             if (client == NULL) break;
1056da142a6fSMilanka Ringwald 
1057f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1058f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10598cec2b74SMilanka Ringwald 
1060556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10618cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1062f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1063556456ccSMilanka Ringwald #endif
1064f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1065f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1066da142a6fSMilanka Ringwald                 if (!stored){
1067da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1068da142a6fSMilanka Ringwald                     break;
1069da142a6fSMilanka Ringwald                 }
1070da142a6fSMilanka Ringwald             }
1071e7bd2dbeSMilanka Ringwald             break;
1072e7bd2dbeSMilanka Ringwald 
107370093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
107470093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
107517229983SMatthias Ringwald             if (client == NULL) break;
107617229983SMatthias Ringwald 
107770093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
107870093cf5SMilanka Ringwald 
1079e7bd2dbeSMilanka Ringwald             switch (client->state) {
1080e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1081e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1082e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
108319146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10843322b222SMilanka Ringwald 
1085556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1086556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1087556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1088556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1089556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1090556456ccSMilanka Ringwald                                 client->service_index, report_index);
1091556456ccSMilanka Ringwald                         }
1092556456ccSMilanka Ringwald #endif
1093e7bd2dbeSMilanka Ringwald                     }
1094e7bd2dbeSMilanka Ringwald                     break;
109528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
109670093cf5SMilanka Ringwald                     // setup for descriptor value query
109770093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1098af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1099556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1100556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1101556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1102556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
110319146789SMilanka Ringwald #endif
1104556456ccSMilanka Ringwald                     }
1105556456ccSMilanka Ringwald 
110619146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1107556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
110883d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1109556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1110556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1111556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1112556456ccSMilanka Ringwald                     }
1113556456ccSMilanka Ringwald #endif
111470093cf5SMilanka Ringwald                     break;
1115556456ccSMilanka Ringwald 
1116e7bd2dbeSMilanka Ringwald                 default:
1117e7bd2dbeSMilanka Ringwald                     break;
1118e7bd2dbeSMilanka Ringwald             }
1119e7bd2dbeSMilanka Ringwald             break;
112070093cf5SMilanka Ringwald 
112183d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
112283d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
112317229983SMatthias Ringwald             if (client == NULL) break;
112483d7ed1cSMilanka Ringwald 
1125f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1126f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1127f4d3b82aSMilanka Ringwald 
1128af2241c2SMilanka Ringwald 
1129f4d3b82aSMilanka Ringwald             switch (client->state){
1130f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1131f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
113283d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1133f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
113483d7ed1cSMilanka Ringwald                     break;
113583d7ed1cSMilanka Ringwald #endif
1136af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1137af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1138af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1139f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1140f4d3b82aSMilanka Ringwald                         break;
1141af2241c2SMilanka Ringwald                     }
1142af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1143af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1144af2241c2SMilanka Ringwald                         break;
1145af2241c2SMilanka Ringwald                     }
1146af2241c2SMilanka Ringwald                     break;
1147af2241c2SMilanka Ringwald                 }
1148f4d3b82aSMilanka Ringwald                 default:
1149f4d3b82aSMilanka Ringwald                     break;
1150f4d3b82aSMilanka Ringwald             }
1151f4d3b82aSMilanka Ringwald 
1152f4d3b82aSMilanka Ringwald             break;
115383d7ed1cSMilanka Ringwald 
115470093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
115570093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
115617229983SMatthias Ringwald             if (client == NULL) break;
1157e7bd2dbeSMilanka Ringwald 
1158e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1159e7bd2dbeSMilanka Ringwald                 break;
1160e7bd2dbeSMilanka Ringwald             }
11617e1e6e7dSMilanka Ringwald 
1162e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1163e7bd2dbeSMilanka Ringwald             switch (client->state) {
116428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1165e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
116619146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11673322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
116819146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1169556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
117019146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
117119146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1172556456ccSMilanka Ringwald #endif
11733322b222SMilanka Ringwald                     }
1174e7bd2dbeSMilanka Ringwald                     break;
1175e7bd2dbeSMilanka Ringwald 
117628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
117719146789SMilanka Ringwald 
117819146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1179021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1180021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
118119146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
118283d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1183021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1184021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
118583d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
118619146789SMilanka Ringwald     #endif
118719146789SMilanka Ringwald                     }
1188e7bd2dbeSMilanka Ringwald                     break;
1189e7bd2dbeSMilanka Ringwald 
1190e7bd2dbeSMilanka Ringwald                 default:
1191e7bd2dbeSMilanka Ringwald                     break;
119270093cf5SMilanka Ringwald             }
119370093cf5SMilanka Ringwald             break;
119470093cf5SMilanka Ringwald 
1195cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1196cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
119717229983SMatthias Ringwald             if (client == NULL) break;
1198cf26c8fbSMilanka Ringwald 
1199cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1200cf26c8fbSMilanka Ringwald 
1201cf26c8fbSMilanka Ringwald             switch (client->state){
1202cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
12036bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12046bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12056bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12065fa2c39aSMilanka Ringwald                         return;
12076bcfb631SMilanka Ringwald                     }
12086bcfb631SMilanka Ringwald 
12096bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12106bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12116bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12125fa2c39aSMilanka Ringwald                         return;
12136bcfb631SMilanka Ringwald                     }
12146bcfb631SMilanka Ringwald 
12156bcfb631SMilanka Ringwald                     client->service_index = 0;
121619146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12176bcfb631SMilanka Ringwald                     break;
12186bcfb631SMilanka Ringwald 
12196bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
12206bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12216bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12226bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12235fa2c39aSMilanka Ringwald                         return;
12246bcfb631SMilanka Ringwald                     }
12256bcfb631SMilanka Ringwald 
122670093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
122770093cf5SMilanka Ringwald                         // discover characteristics of next service
122870093cf5SMilanka Ringwald                         client->service_index++;
122970093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1230021192e1SMilanka Ringwald                         break;
1231021192e1SMilanka Ringwald                     }
1232021192e1SMilanka Ringwald 
1233fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1234fd39e93aSMilanka Ringwald 
1235021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1236021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1237fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1238fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1239021192e1SMilanka Ringwald                             }
12407e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12417e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12427e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
124370093cf5SMilanka Ringwald                                 break;
124470093cf5SMilanka Ringwald                             }
124570093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
124670093cf5SMilanka Ringwald                             hids_finalize_client(client);
1247fd39e93aSMilanka Ringwald                             return;
1248fd39e93aSMilanka Ringwald 
1249fd39e93aSMilanka Ringwald                         default:
1250fd39e93aSMilanka Ringwald                             // set boot mode
1251fd39e93aSMilanka Ringwald                             client->service_index = 0;
1252fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1253fd39e93aSMilanka Ringwald                             break;
1254fd39e93aSMilanka Ringwald                     }
12556bcfb631SMilanka Ringwald                     break;
12566bcfb631SMilanka Ringwald 
12573322b222SMilanka Ringwald 
125828da36a6SMilanka Ringwald                 // HID descriptor found
125928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1260e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1261e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1262e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12635fa2c39aSMilanka Ringwald                         return;
1264e7bd2dbeSMilanka Ringwald                     }
1265e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1266e7bd2dbeSMilanka Ringwald                     break;
1267e7bd2dbeSMilanka Ringwald 
126828da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1269e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12703322b222SMilanka Ringwald                     // go for next report map
12713322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1272e7bd2dbeSMilanka Ringwald                         break;
1273e7bd2dbeSMilanka Ringwald                     }
1274e7bd2dbeSMilanka Ringwald 
12753322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12763322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1277e7bd2dbeSMilanka Ringwald                         break;
1278e7bd2dbeSMilanka Ringwald                     }
1279e7bd2dbeSMilanka Ringwald 
1280e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1281e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12827e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1283e7bd2dbeSMilanka Ringwald                         break;
1284e7bd2dbeSMilanka Ringwald                     }
1285e7bd2dbeSMilanka Ringwald 
1286e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1287e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
12885fa2c39aSMilanka Ringwald                     return;
1289e7bd2dbeSMilanka Ringwald 
129028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1291e7bd2dbeSMilanka Ringwald                     // go for next map report
12923322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1293e7bd2dbeSMilanka Ringwald                         break;
1294e7bd2dbeSMilanka Ringwald                     }
1295e7bd2dbeSMilanka Ringwald 
12963322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12973322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1298e7bd2dbeSMilanka Ringwald                     break;
1299e7bd2dbeSMilanka Ringwald 
13003322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1301e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1302e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13037e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1304e7bd2dbeSMilanka Ringwald                         break;
1305e7bd2dbeSMilanka Ringwald                     }
1306e7bd2dbeSMilanka Ringwald 
1307e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1308e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13095fa2c39aSMilanka Ringwald                     return;
1310e7bd2dbeSMilanka Ringwald 
131128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1312af2241c2SMilanka Ringwald                     if (client->handle != 0){
131328da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
131470093cf5SMilanka Ringwald                         break;
131570093cf5SMilanka Ringwald                     }
131670093cf5SMilanka Ringwald                     // go for next report
13177e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
131870093cf5SMilanka Ringwald                         break;
131970093cf5SMilanka Ringwald                     }
1320835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
132170093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
132270093cf5SMilanka Ringwald                     break;
132370093cf5SMilanka Ringwald 
132428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
132570093cf5SMilanka Ringwald                     // go for next report
13267e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
132770093cf5SMilanka Ringwald                         break;
132870093cf5SMilanka Ringwald                     }
1329021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1330021192e1SMilanka Ringwald                         break;
1331021192e1SMilanka Ringwald                     }
1332835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
133370093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
133470093cf5SMilanka Ringwald                     break;
133570093cf5SMilanka Ringwald 
1336021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1337021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13382901a9b7SMilanka Ringwald                         break;
13392901a9b7SMilanka Ringwald                     }
1340835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13416bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13426bcfb631SMilanka Ringwald                     break;
1343f4d3b82aSMilanka Ringwald 
1344cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1345cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1346cd28e7b1SMilanka Ringwald                         break;
1347cd28e7b1SMilanka Ringwald                     }
1348cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1349cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1350cd28e7b1SMilanka Ringwald                     break;
1351cd28e7b1SMilanka Ringwald 
1352835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1353835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1354835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1355835a13f1SMilanka Ringwald                     break;
1356835a13f1SMilanka Ringwald #endif
1357f4d3b82aSMilanka Ringwald 
1358af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1359f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1360f4d3b82aSMilanka Ringwald                     break;
1361f4d3b82aSMilanka Ringwald 
1362*84d4ab66SMatthias Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1363*84d4ab66SMatthias Ringwald                     {
1364*84d4ab66SMatthias Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
1365*84d4ab66SMatthias Ringwald 
1366*84d4ab66SMatthias Ringwald                         // emit empty report to signal done
1367*84d4ab66SMatthias Ringwald                         uint8_t event[9];
1368*84d4ab66SMatthias Ringwald                         hids_client_setup_report_event(client, client->report_index, event, 0);
1369*84d4ab66SMatthias Ringwald                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1370*84d4ab66SMatthias Ringwald                     }
1371*84d4ab66SMatthias Ringwald                     break;
13726d6f7efcSMilanka Ringwald 
1373cf26c8fbSMilanka Ringwald                 default:
1374cf26c8fbSMilanka Ringwald                     break;
1375cf26c8fbSMilanka Ringwald             }
1376cf26c8fbSMilanka Ringwald             break;
1377cf26c8fbSMilanka Ringwald 
1378cf26c8fbSMilanka Ringwald         default:
1379cf26c8fbSMilanka Ringwald             break;
1380cf26c8fbSMilanka Ringwald     }
13816bcfb631SMilanka Ringwald 
13826bcfb631SMilanka Ringwald     if (client != NULL){
13836bcfb631SMilanka Ringwald         hids_run_for_client(client);
13846bcfb631SMilanka Ringwald     }
1385cf26c8fbSMilanka Ringwald }
1386cf26c8fbSMilanka Ringwald 
13876bcfb631SMilanka 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){
1388cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1389cf26c8fbSMilanka Ringwald 
1390cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1391cf26c8fbSMilanka Ringwald     if (client != NULL){
1392cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1393cf26c8fbSMilanka Ringwald     }
1394cf26c8fbSMilanka Ringwald 
1395cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1396cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1397cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1398cf26c8fbSMilanka Ringwald     }
1399cf26c8fbSMilanka Ringwald 
1400cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1401cf26c8fbSMilanka Ringwald     if (client == NULL) {
1402cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1403cf26c8fbSMilanka Ringwald     }
1404cf26c8fbSMilanka Ringwald 
14056bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1406cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1407cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1408cf26c8fbSMilanka Ringwald 
1409cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1410cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1411cf26c8fbSMilanka Ringwald }
1412cf26c8fbSMilanka Ringwald 
1413cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1414cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1415cf26c8fbSMilanka Ringwald     if (client == NULL){
1416cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1417cf26c8fbSMilanka Ringwald     }
1418cf26c8fbSMilanka Ringwald     // finalize connection
1419cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1420cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1421cf26c8fbSMilanka Ringwald }
1422cf26c8fbSMilanka Ringwald 
1423fd39e93aSMilanka 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){
14241624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14251624214bSMilanka Ringwald     if (client == NULL){
14261624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14271624214bSMilanka Ringwald     }
14281624214bSMilanka Ringwald 
14291624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14301624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14311624214bSMilanka Ringwald     }
14321624214bSMilanka Ringwald 
1433fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
143483d7ed1cSMilanka Ringwald 
14351624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14361624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14371624214bSMilanka Ringwald     }
14381624214bSMilanka Ringwald 
14391624214bSMilanka Ringwald     uint16_t mtu;
14401624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14411624214bSMilanka Ringwald 
14421624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14431624214bSMilanka Ringwald         return status;
14441624214bSMilanka Ringwald     }
14451624214bSMilanka Ringwald 
14461624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14471624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14481624214bSMilanka Ringwald     }
14491624214bSMilanka Ringwald 
14506d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1451021192e1SMilanka Ringwald     client->report_index = report_index;
14521624214bSMilanka Ringwald     client->report = report;
14531624214bSMilanka Ringwald     client->report_len = report_len;
14541624214bSMilanka Ringwald 
14551624214bSMilanka Ringwald     hids_run_for_client(client);
14561624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14571624214bSMilanka Ringwald }
14581624214bSMilanka Ringwald 
1459fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
146083d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
146183d7ed1cSMilanka Ringwald     if (client == NULL){
146283d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
146383d7ed1cSMilanka Ringwald     }
146483d7ed1cSMilanka Ringwald 
146583d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
146683d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
146783d7ed1cSMilanka Ringwald     }
146883d7ed1cSMilanka Ringwald 
1469fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
147083d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
147183d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
147283d7ed1cSMilanka Ringwald     }
147383d7ed1cSMilanka Ringwald 
147483d7ed1cSMilanka Ringwald     client->report_index = report_index;
147583d7ed1cSMilanka Ringwald 
147683d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
147783d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
147883d7ed1cSMilanka Ringwald #else
147983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
148083d7ed1cSMilanka Ringwald #endif
148183d7ed1cSMilanka Ringwald     hids_run_for_client(client);
148283d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
148383d7ed1cSMilanka Ringwald }
148483d7ed1cSMilanka Ringwald 
148583d7ed1cSMilanka Ringwald 
1486f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1487f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1488f4d3b82aSMilanka Ringwald     if (client == NULL){
1489f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1490f4d3b82aSMilanka Ringwald     }
1491f4d3b82aSMilanka Ringwald 
1492f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1493f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1494f4d3b82aSMilanka Ringwald     }
1495f4d3b82aSMilanka Ringwald 
1496f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1497f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1498f4d3b82aSMilanka Ringwald     }
1499f4d3b82aSMilanka Ringwald 
1500f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1501af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1502af2241c2SMilanka Ringwald 
1503af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1504af2241c2SMilanka Ringwald     hids_run_for_client(client);
1505af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1506af2241c2SMilanka Ringwald }
1507af2241c2SMilanka Ringwald 
1508af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1509af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1510af2241c2SMilanka Ringwald     if (client == NULL){
1511af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1512af2241c2SMilanka Ringwald     }
1513af2241c2SMilanka Ringwald 
1514af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1515af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1516af2241c2SMilanka Ringwald     }
1517af2241c2SMilanka Ringwald 
1518af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1519af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1520af2241c2SMilanka Ringwald     }
1521af2241c2SMilanka Ringwald 
1522af2241c2SMilanka Ringwald     client->service_index = service_index;
1523af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1524af2241c2SMilanka Ringwald 
1525af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1526f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1527f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1528f4d3b82aSMilanka Ringwald }
1529f4d3b82aSMilanka Ringwald 
15300cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15316d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15326d6f7efcSMilanka Ringwald     if (client == NULL){
15336d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15346d6f7efcSMilanka Ringwald     }
15356d6f7efcSMilanka Ringwald 
15366d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15376d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15386d6f7efcSMilanka Ringwald     }
15396d6f7efcSMilanka Ringwald 
15406d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15416d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15426d6f7efcSMilanka Ringwald     }
15436d6f7efcSMilanka Ringwald 
15446d6f7efcSMilanka Ringwald     client->service_index = service_index;
15456d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15466d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15476d6f7efcSMilanka Ringwald 
15486d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15496d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15506d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15516d6f7efcSMilanka Ringwald }
15526d6f7efcSMilanka Ringwald 
15536d6f7efcSMilanka Ringwald 
15546d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15556d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15566d6f7efcSMilanka Ringwald     if (client == NULL){
15576d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15586d6f7efcSMilanka Ringwald     }
15596d6f7efcSMilanka Ringwald 
15606d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15616d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15626d6f7efcSMilanka Ringwald     }
15636d6f7efcSMilanka Ringwald 
15646d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15656d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15666d6f7efcSMilanka Ringwald     }
15676d6f7efcSMilanka Ringwald 
15686d6f7efcSMilanka Ringwald     client->service_index = service_index;
15696d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15706d6f7efcSMilanka Ringwald     client->value = value;
15716d6f7efcSMilanka Ringwald 
15726d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15736d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15746d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15756d6f7efcSMilanka Ringwald }
15766d6f7efcSMilanka Ringwald 
15776d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15786d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15796d6f7efcSMilanka Ringwald }
15806d6f7efcSMilanka Ringwald 
15816d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
15826d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
15836d6f7efcSMilanka Ringwald }
15846d6f7efcSMilanka Ringwald 
15850cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1586cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1587cd28e7b1SMilanka Ringwald     if (client == NULL){
1588cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1589cd28e7b1SMilanka Ringwald     }
1590cd28e7b1SMilanka Ringwald 
1591cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1592cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1593cd28e7b1SMilanka Ringwald     }
1594cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1595cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1596cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1597cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1598cd28e7b1SMilanka Ringwald     }
1599cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1600cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1601cd28e7b1SMilanka Ringwald }
1602cd28e7b1SMilanka Ringwald 
16030cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1604cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1605cd28e7b1SMilanka Ringwald     if (client == NULL){
1606cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1607cd28e7b1SMilanka Ringwald     }
1608cd28e7b1SMilanka Ringwald 
1609cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1610cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1611cd28e7b1SMilanka Ringwald     }
1612cd28e7b1SMilanka Ringwald 
1613cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1614cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1615cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1616cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1617cd28e7b1SMilanka Ringwald     }
1618cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1619cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1620cd28e7b1SMilanka Ringwald }
16216d6f7efcSMilanka Ringwald 
1622021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1623021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1624021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1625021192e1SMilanka Ringwald }
1626cf26c8fbSMilanka Ringwald 
1627cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1628