xref: /btstack/src/ble/gatt-service/hids_client.c (revision 6d6f7efc52cfa7d32062e0a238348bc17310757e)
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
23fc975d0eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24fc975d0eSMilanka Ringwald  * RINGWALD 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/att_db.h"
53fc975d0eSMilanka Ringwald #include "ble/core.h"
54fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
55fc975d0eSMilanka Ringwald #include "ble/sm.h"
56cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
57fc975d0eSMilanka Ringwald #include "btstack_debug.h"
58fc975d0eSMilanka Ringwald #include "btstack_event.h"
59fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
60fc975d0eSMilanka Ringwald #include "gap.h"
61fc975d0eSMilanka Ringwald 
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6570093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6670093cf5SMilanka Ringwald 
67da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
68da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
69da142a6fSMilanka Ringwald 
70da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
71da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
72da142a6fSMilanka Ringwald 
73cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
74cf26c8fbSMilanka Ringwald 
75556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
76556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
77556456ccSMilanka Ringwald     switch (uuid){
78556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
79556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
80556456ccSMilanka Ringwald 
81556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
82556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
83556456ccSMilanka Ringwald 
84556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
85556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
86556456ccSMilanka Ringwald 
87556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
88556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
89556456ccSMilanka Ringwald 
90556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
91556456ccSMilanka Ringwald             return "REPORT";
92556456ccSMilanka Ringwald 
93556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94556456ccSMilanka Ringwald             return "REPORT_MAP";
95556456ccSMilanka Ringwald 
96556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
97556456ccSMilanka Ringwald             return "HID_INFORMATION";
98556456ccSMilanka Ringwald 
99556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
100556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
101556456ccSMilanka Ringwald         default:
102556456ccSMilanka Ringwald             return "UKNOWN";
103556456ccSMilanka Ringwald     }
104556456ccSMilanka Ringwald }
105556456ccSMilanka Ringwald #endif
106556456ccSMilanka Ringwald 
107da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
109da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
110da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
111da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
112da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
113da142a6fSMilanka Ringwald         return client;
114da142a6fSMilanka Ringwald     }
115da142a6fSMilanka Ringwald     return NULL;
116da142a6fSMilanka Ringwald }
117da142a6fSMilanka Ringwald 
118da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
120da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
121da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
122da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
123da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
124da142a6fSMilanka Ringwald         return client;
125da142a6fSMilanka Ringwald     }
126da142a6fSMilanka Ringwald     return NULL;
127da142a6fSMilanka Ringwald }
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald // START Descriptor Storage Util
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
133da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
134da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
135da142a6fSMilanka Ringwald     uint8_t i;
136da142a6fSMilanka Ringwald 
137da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
138da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
139da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
140da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
141da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
142da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
143da142a6fSMilanka Ringwald         }
144da142a6fSMilanka Ringwald     }
145da142a6fSMilanka Ringwald     return free_space;
146da142a6fSMilanka Ringwald }
147da142a6fSMilanka Ringwald 
148da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
150da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
151da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
155da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
156da142a6fSMilanka Ringwald 
157da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
159da142a6fSMilanka Ringwald     return true;
160da142a6fSMilanka Ringwald }
161da142a6fSMilanka Ringwald 
162da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
163da142a6fSMilanka Ringwald     uint8_t service_index = 0;
164da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
165da142a6fSMilanka Ringwald 
166da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
167da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
168da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
169da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
170da142a6fSMilanka Ringwald     }
171da142a6fSMilanka Ringwald 
172da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
173da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
174da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
175da142a6fSMilanka Ringwald 
176da142a6fSMilanka Ringwald     uint8_t i;
177da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
178da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
179da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
180da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
181da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
182da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
183da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
184da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
185da142a6fSMilanka Ringwald             }
186da142a6fSMilanka Ringwald         }
187da142a6fSMilanka Ringwald     }
188da142a6fSMilanka Ringwald }
189da142a6fSMilanka Ringwald 
190da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
191da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
192021192e1SMilanka Ringwald     if (client == NULL){
193021192e1SMilanka Ringwald         return NULL;
194021192e1SMilanka Ringwald     }
195021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
196da142a6fSMilanka Ringwald         return NULL;
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
199da142a6fSMilanka Ringwald }
200da142a6fSMilanka Ringwald 
201da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
202da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
203021192e1SMilanka Ringwald     if (client == NULL){
204021192e1SMilanka Ringwald         return 0;
205021192e1SMilanka Ringwald     }
206021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
207da142a6fSMilanka Ringwald         return 0;
208da142a6fSMilanka Ringwald     }
209da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
210da142a6fSMilanka Ringwald }
211da142a6fSMilanka Ringwald 
212da142a6fSMilanka Ringwald // END Descriptor Storage Util
213da142a6fSMilanka Ringwald 
214cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
215cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
216cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
217cf26c8fbSMilanka Ringwald     } else {
218cf26c8fbSMilanka Ringwald         hids_cid_counter++;
219cf26c8fbSMilanka Ringwald     }
220cf26c8fbSMilanka Ringwald     return hids_cid_counter;
221fc975d0eSMilanka Ringwald }
222fc975d0eSMilanka Ringwald 
223ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
224ab116b1cSMilanka Ringwald     uint8_t i;
225a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
226ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
227ab116b1cSMilanka Ringwald             return i;
228ab116b1cSMilanka Ringwald         }
229ab116b1cSMilanka Ringwald     }
230ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
231ab116b1cSMilanka Ringwald }
232ab116b1cSMilanka Ringwald 
23319146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
23419146789SMilanka Ringwald     uint8_t i;
23519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
23619146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
23719146789SMilanka Ringwald             return i;
23819146789SMilanka Ringwald         }
23919146789SMilanka Ringwald     }
24019146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
24119146789SMilanka Ringwald }
24219146789SMilanka Ringwald 
24319146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
24419146789SMilanka Ringwald     uint8_t i;
24519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24619146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
24719146789SMilanka Ringwald             return true;
24819146789SMilanka Ringwald         }
24919146789SMilanka Ringwald     }
25019146789SMilanka Ringwald     return false;
25119146789SMilanka Ringwald }
25219146789SMilanka Ringwald 
253835a13f1SMilanka Ringwald static uint8_t find_report_index_for_report_id(hids_client_t * client, uint8_t report_id){
25483d7ed1cSMilanka Ringwald     uint8_t i;
255835a13f1SMilanka Ringwald 
25683d7ed1cSMilanka Ringwald     switch (client->protocol_mode){
25783d7ed1cSMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
25883d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
25983d7ed1cSMilanka Ringwald                 if (!client->reports[i].boot_report){
26083d7ed1cSMilanka Ringwald                     continue;
26183d7ed1cSMilanka Ringwald                 }
262835a13f1SMilanka Ringwald                 if (client->reports[i].report_id == report_id){
26383d7ed1cSMilanka Ringwald                     return i;
26483d7ed1cSMilanka Ringwald                 }
26583d7ed1cSMilanka Ringwald             }
26683d7ed1cSMilanka Ringwald             break;
26783d7ed1cSMilanka Ringwald 
26883d7ed1cSMilanka Ringwald         default:
26983d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
27083d7ed1cSMilanka Ringwald                 if (client->reports[i].boot_report){
27183d7ed1cSMilanka Ringwald                     continue;
27283d7ed1cSMilanka Ringwald                 }
273835a13f1SMilanka Ringwald                 if (client->reports[i].report_id == report_id){
27483d7ed1cSMilanka Ringwald                     return i;
27583d7ed1cSMilanka Ringwald                 }
27683d7ed1cSMilanka Ringwald             }
27783d7ed1cSMilanka Ringwald             break;
27883d7ed1cSMilanka Ringwald     }
27983d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
28083d7ed1cSMilanka Ringwald }
28119146789SMilanka Ringwald 
282021192e1SMilanka 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){
283ab116b1cSMilanka Ringwald 
28419146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
285ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2863322b222SMilanka Ringwald         return report_index;
287ab116b1cSMilanka Ringwald     }
28828da36a6SMilanka Ringwald     report_index = client->num_reports;
289ab116b1cSMilanka Ringwald 
29028da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
29128da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
29228da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
29328da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
294ab116b1cSMilanka Ringwald 
29528da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
29628da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
29728da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
298021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
29970093cf5SMilanka Ringwald 
300021192e1SMilanka 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);
301ab116b1cSMilanka Ringwald         client->num_reports++;
3023322b222SMilanka Ringwald         return report_index;
303ab116b1cSMilanka Ringwald     } else {
304ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3053322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
306ab116b1cSMilanka Ringwald     }
307ab116b1cSMilanka Ringwald }
308ab116b1cSMilanka Ringwald 
30919146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
310556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
311556456ccSMilanka Ringwald 
312556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
31319146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
314556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
31519146789SMilanka Ringwald 
316556456ccSMilanka Ringwald         client->num_external_reports++;
31719146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
318556456ccSMilanka Ringwald         return report_index;
319556456ccSMilanka Ringwald     } else {
320556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
321556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
32270093cf5SMilanka Ringwald     }
323556456ccSMilanka Ringwald }
324556456ccSMilanka Ringwald 
32570093cf5SMilanka Ringwald 
326021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
327021192e1SMilanka Ringwald     uint8_t i;
328021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
329021192e1SMilanka Ringwald         if (!client->reports[i].boot_report){
330021192e1SMilanka Ringwald             return true;
331ab116b1cSMilanka Ringwald         }
332021192e1SMilanka Ringwald     }
333021192e1SMilanka Ringwald     return false;
334021192e1SMilanka Ringwald }
335021192e1SMilanka Ringwald 
336021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
337021192e1SMilanka Ringwald     uint8_t i;
338021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
339021192e1SMilanka Ringwald         if (client->reports[i].boot_report){
340021192e1SMilanka Ringwald             return true;
341021192e1SMilanka Ringwald         }
342021192e1SMilanka Ringwald     }
343021192e1SMilanka Ringwald     return false;
344ab116b1cSMilanka Ringwald }
345ab116b1cSMilanka Ringwald 
3467e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3477e1e6e7dSMilanka Ringwald     uint8_t i;
3483322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3493322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
350556456ccSMilanka Ringwald             return i;
3513322b222SMilanka Ringwald         }
3523322b222SMilanka Ringwald     }
353556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
354556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3553322b222SMilanka Ringwald }
3563322b222SMilanka Ringwald 
3573322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3583322b222SMilanka Ringwald     client->service_index++;
3593322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
360556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3613322b222SMilanka Ringwald         return true;
3623322b222SMilanka Ringwald     }
3633322b222SMilanka Ringwald     return false;
3643322b222SMilanka Ringwald }
3653322b222SMilanka Ringwald 
3663322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3673322b222SMilanka Ringwald     client->service_index = 0;
3683322b222SMilanka Ringwald 
3693322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3703322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3713322b222SMilanka Ringwald         return true;
3723322b222SMilanka Ringwald     }
3733322b222SMilanka Ringwald     return false;
3743322b222SMilanka Ringwald }
3753322b222SMilanka Ringwald 
3763322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
377021192e1SMilanka Ringwald     client->report_index++;
37819146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3793322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
380e7bd2dbeSMilanka Ringwald         return true;
381e7bd2dbeSMilanka Ringwald     }
382e7bd2dbeSMilanka Ringwald     return false;
383e7bd2dbeSMilanka Ringwald }
384e7bd2dbeSMilanka Ringwald 
3853322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
386021192e1SMilanka Ringwald     client->report_index = 0;
38719146789SMilanka Ringwald     if (client->num_external_reports > 0){
3883322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3897e1e6e7dSMilanka Ringwald         return true;
3907e1e6e7dSMilanka Ringwald     }
3917e1e6e7dSMilanka Ringwald     return false;
3927e1e6e7dSMilanka Ringwald }
3937e1e6e7dSMilanka Ringwald 
3947e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3957e1e6e7dSMilanka Ringwald     uint8_t i;
3967e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
397021192e1SMilanka Ringwald     switch (client->protocol_mode){
398021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
399021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
40070093cf5SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
40170093cf5SMilanka Ringwald                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
4027e1e6e7dSMilanka Ringwald                     index = i;
403021192e1SMilanka Ringwald                     client->service_index = report.service_index;
404021192e1SMilanka Ringwald                 }
405021192e1SMilanka Ringwald             }
406021192e1SMilanka Ringwald             break;
407021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
408021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
409021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
410021192e1SMilanka Ringwald                 if (report.boot_report){
411021192e1SMilanka Ringwald                     index = i;
412021192e1SMilanka Ringwald                     client->service_index = report.service_index;
413021192e1SMilanka Ringwald                 }
414021192e1SMilanka Ringwald             }
415021192e1SMilanka Ringwald             break;
416021192e1SMilanka Ringwald         default:
4177e1e6e7dSMilanka Ringwald             break;
41870093cf5SMilanka Ringwald     }
419021192e1SMilanka Ringwald 
420021192e1SMilanka Ringwald     client->report_index = index;
4217e1e6e7dSMilanka Ringwald     return index;
4227e1e6e7dSMilanka Ringwald }
4237e1e6e7dSMilanka Ringwald 
4247e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
425021192e1SMilanka Ringwald     client->report_index++;
4267e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
42728da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
428e7bd2dbeSMilanka Ringwald         return true;
42970093cf5SMilanka Ringwald     }
4307e1e6e7dSMilanka Ringwald     return false;
4317e1e6e7dSMilanka Ringwald }
4327e1e6e7dSMilanka Ringwald 
4337e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
434021192e1SMilanka Ringwald     client->report_index = 0;
4357e1e6e7dSMilanka Ringwald 
4367e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
43728da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4387e1e6e7dSMilanka Ringwald         return true;
4397e1e6e7dSMilanka Ringwald     }
440e7bd2dbeSMilanka Ringwald     return false;
44170093cf5SMilanka Ringwald }
442ab116b1cSMilanka Ringwald 
443021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
444021192e1SMilanka Ringwald     uint8_t i;
445021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
446021192e1SMilanka Ringwald 
447021192e1SMilanka Ringwald     switch (client->protocol_mode){
448021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
449021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
450021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
451021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
452021192e1SMilanka Ringwald                     continue;
453021192e1SMilanka Ringwald                 }
454021192e1SMilanka Ringwald                 if (!report.boot_report){
455021192e1SMilanka Ringwald                     index = i;
456021192e1SMilanka Ringwald                 }
457021192e1SMilanka Ringwald             }
458021192e1SMilanka Ringwald             break;
459021192e1SMilanka Ringwald 
460021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
461021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
462021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
463021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
464021192e1SMilanka Ringwald                     continue;
465021192e1SMilanka Ringwald                 }
466021192e1SMilanka Ringwald                 if (report.boot_report){
467021192e1SMilanka Ringwald                     index = i;
468021192e1SMilanka Ringwald                 }
469021192e1SMilanka Ringwald             }
470021192e1SMilanka Ringwald             break;
471021192e1SMilanka Ringwald 
472021192e1SMilanka Ringwald         default:
473021192e1SMilanka Ringwald             break;
474021192e1SMilanka Ringwald     }
475021192e1SMilanka Ringwald 
476021192e1SMilanka Ringwald     client->report_index = index;
477021192e1SMilanka Ringwald     return index;
478021192e1SMilanka Ringwald }
479021192e1SMilanka Ringwald 
480021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
481021192e1SMilanka Ringwald     client->report_index++;
482021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
483021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
484021192e1SMilanka Ringwald         return true;
485021192e1SMilanka Ringwald     }
486021192e1SMilanka Ringwald     return false;
487021192e1SMilanka Ringwald }
488021192e1SMilanka Ringwald 
489021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
490021192e1SMilanka Ringwald     client->report_index = 0;
491021192e1SMilanka Ringwald 
492021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
493021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
494021192e1SMilanka Ringwald         return true;
495021192e1SMilanka Ringwald     }
496021192e1SMilanka Ringwald     return false;
497021192e1SMilanka Ringwald }
498021192e1SMilanka Ringwald 
499cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
500cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
501cf26c8fbSMilanka Ringwald     if (!client){
502cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
503cf26c8fbSMilanka Ringwald         return NULL;
504cf26c8fbSMilanka Ringwald     }
505cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
506cf26c8fbSMilanka Ringwald     client->cid = cid;
507cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
508fc975d0eSMilanka Ringwald 
509cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
510cf26c8fbSMilanka Ringwald     return client;
511fc975d0eSMilanka Ringwald }
512fc975d0eSMilanka Ringwald 
513cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
514021192e1SMilanka Ringwald     // stop listening
515021192e1SMilanka Ringwald     uint8_t i;
516021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
517021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
518021192e1SMilanka Ringwald     }
519021192e1SMilanka Ringwald 
520da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
521cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
522cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
523fc975d0eSMilanka Ringwald }
524cf26c8fbSMilanka Ringwald 
525cf26c8fbSMilanka Ringwald 
526cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5276bcfb631SMilanka Ringwald     uint8_t event[8];
528cf26c8fbSMilanka Ringwald     int pos = 0;
529cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
530cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
531cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
532cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
533cf26c8fbSMilanka Ringwald     pos += 2;
534cf26c8fbSMilanka Ringwald     event[pos++] = status;
5356bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
536cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
537cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
538cf26c8fbSMilanka Ringwald }
539cf26c8fbSMilanka Ringwald 
540021192e1SMilanka Ringwald static void 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;
549021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
550021192e1SMilanka Ringwald     pos += 2;
551021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
55283d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
55383d7ed1cSMilanka Ringwald 
554021192e1SMilanka Ringwald }
555021192e1SMilanka Ringwald 
556f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
557f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
558f4d3b82aSMilanka Ringwald 
559f4d3b82aSMilanka Ringwald     uint8_t event[11];
560f4d3b82aSMilanka Ringwald     int pos = 0;
561f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
562f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
563f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
564f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
565f4d3b82aSMilanka Ringwald     pos += 2;
566f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
567f4d3b82aSMilanka Ringwald 
568f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
569f4d3b82aSMilanka Ringwald     pos += 3;
570f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
571f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
572f4d3b82aSMilanka Ringwald 
573f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
574f4d3b82aSMilanka Ringwald }
575f4d3b82aSMilanka Ringwald 
576af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
577af2241c2SMilanka Ringwald     if (value_len != 1) return;
578af2241c2SMilanka Ringwald 
579af2241c2SMilanka Ringwald     uint8_t event[11];
580af2241c2SMilanka Ringwald     int pos = 0;
581af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
582af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
583af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
584af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
585af2241c2SMilanka Ringwald     pos += 2;
586af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
587af2241c2SMilanka Ringwald     event[pos++] = value[0];
588af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
589af2241c2SMilanka Ringwald }
590f4d3b82aSMilanka Ringwald 
59183d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
592021192e1SMilanka Ringwald     UNUSED(packet_type);
593021192e1SMilanka Ringwald     UNUSED(channel);
59483d7ed1cSMilanka Ringwald 
595021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
596021192e1SMilanka Ringwald 
597021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
598021192e1SMilanka Ringwald     btstack_assert(client != NULL);
599021192e1SMilanka Ringwald 
600021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
601021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
602021192e1SMilanka Ringwald         return;
603021192e1SMilanka Ringwald     }
604021192e1SMilanka Ringwald 
60583d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
606021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
60783d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
608021192e1SMilanka Ringwald }
609021192e1SMilanka Ringwald 
61083d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
61183d7ed1cSMilanka Ringwald     UNUSED(packet_type);
61283d7ed1cSMilanka Ringwald     UNUSED(channel);
61383d7ed1cSMilanka Ringwald 
61483d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
61583d7ed1cSMilanka Ringwald 
61683d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
61783d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
61883d7ed1cSMilanka Ringwald 
61983d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
62083d7ed1cSMilanka Ringwald         return;
62183d7ed1cSMilanka Ringwald     }
62283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
62383d7ed1cSMilanka Ringwald 
62483d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
62583d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
62683d7ed1cSMilanka Ringwald         return;
62783d7ed1cSMilanka Ringwald     }
62883d7ed1cSMilanka Ringwald 
62983d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
63083d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
63183d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
63283d7ed1cSMilanka Ringwald }
633cf26c8fbSMilanka Ringwald 
634cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
635cf26c8fbSMilanka Ringwald     uint8_t att_status;
6366bcfb631SMilanka Ringwald     gatt_client_service_t service;
6376bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
638cf26c8fbSMilanka Ringwald 
639cf26c8fbSMilanka Ringwald     switch (client->state){
640cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
641556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
642556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
643556456ccSMilanka Ringwald #endif
644cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
64519146789SMilanka Ringwald 
64619146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
647cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
648cf26c8fbSMilanka Ringwald             UNUSED(att_status);
649cf26c8fbSMilanka Ringwald             break;
650cf26c8fbSMilanka Ringwald 
651cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
652556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
653556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
654556456ccSMilanka Ringwald #endif
6556bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6566bcfb631SMilanka Ringwald 
6576bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6586bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
65919146789SMilanka Ringwald 
66019146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6616bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6626bcfb631SMilanka Ringwald 
6636bcfb631SMilanka Ringwald             UNUSED(att_status);
6646bcfb631SMilanka Ringwald             break;
6656bcfb631SMilanka Ringwald 
666021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
6672901a9b7SMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
6686bcfb631SMilanka Ringwald             UNUSED(att_status);
6692901a9b7SMilanka Ringwald 
6702901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
671021192e1SMilanka Ringwald             if (hids_client_report_query_init(client)){
672021192e1SMilanka Ringwald                 break;
673021192e1SMilanka Ringwald             }
674021192e1SMilanka Ringwald 
675021192e1SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
676021192e1SMilanka Ringwald             hids_finalize_client(client);
677cf26c8fbSMilanka Ringwald             break;
678cf26c8fbSMilanka Ringwald 
67928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
680556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
681556456ccSMilanka 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);
682556456ccSMilanka Ringwald #endif
68328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
684556456ccSMilanka Ringwald 
68519146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
68619146789SMilanka 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);
687e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
688e7bd2dbeSMilanka Ringwald             break;
689e7bd2dbeSMilanka Ringwald 
690e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
691556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
692556456ccSMilanka 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);
693556456ccSMilanka Ringwald #endif
694e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
695e7bd2dbeSMilanka Ringwald 
6963322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6973322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6983322b222SMilanka Ringwald 
69919146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
700e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
701e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
702e7bd2dbeSMilanka Ringwald             break;
703e7bd2dbeSMilanka Ringwald 
70428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
705556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
70619146789SMilanka 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);
707556456ccSMilanka Ringwald #endif
70828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
709e7bd2dbeSMilanka Ringwald 
71019146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
71119146789SMilanka 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);
712e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
713e7bd2dbeSMilanka Ringwald             break;
714e7bd2dbeSMilanka Ringwald 
7153322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
716556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
71719146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
718556456ccSMilanka Ringwald #endif
7193322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
720e7bd2dbeSMilanka Ringwald 
7213322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7223322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
72319146789SMilanka Ringwald 
72419146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7253322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
726e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
727e7bd2dbeSMilanka Ringwald             break;
728e7bd2dbeSMilanka Ringwald 
72928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
730556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
73183d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
732556456ccSMilanka Ringwald                 client->report_index,
73383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
73483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
735556456ccSMilanka Ringwald #endif
73628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
737af2241c2SMilanka Ringwald             client->handle = 0;
738556456ccSMilanka Ringwald 
739556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
740556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
741556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
742556456ccSMilanka Ringwald 
74319146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
74470093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
74570093cf5SMilanka Ringwald             UNUSED(att_status);
74670093cf5SMilanka Ringwald             break;
74770093cf5SMilanka Ringwald 
74828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
74928da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
75070093cf5SMilanka Ringwald 
75119146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
752af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
753af2241c2SMilanka Ringwald             client->handle = 0;
75470093cf5SMilanka Ringwald             UNUSED(att_status);
75570093cf5SMilanka Ringwald             break;
75670093cf5SMilanka Ringwald 
757021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
758021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
759021192e1SMilanka Ringwald 
760021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
761021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
762021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
763021192e1SMilanka Ringwald 
76419146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
765021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
766021192e1SMilanka Ringwald 
767021192e1SMilanka Ringwald             if (att_status != ERROR_CODE_SUCCESS){
768021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
769021192e1SMilanka Ringwald                     hids_run_for_client(client);
770021192e1SMilanka Ringwald                     break;
771021192e1SMilanka Ringwald                 }
772021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
773021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
774021192e1SMilanka Ringwald             } else {
775021192e1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
776021192e1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
77783d7ed1cSMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
778021192e1SMilanka Ringwald 
779021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
780021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
781021192e1SMilanka Ringwald             }
782021192e1SMilanka Ringwald             UNUSED(att_status);
783021192e1SMilanka Ringwald             break;
784021192e1SMilanka Ringwald 
78583d7ed1cSMilanka Ringwald 
786*6d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
78783d7ed1cSMilanka Ringwald 
78883d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
789*6d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
79083d7ed1cSMilanka Ringwald                 client->report_index,
79183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
79283d7ed1cSMilanka Ringwald #endif
79383d7ed1cSMilanka Ringwald 
794*6d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
79583d7ed1cSMilanka Ringwald 
796*6d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
797*6d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
798*6d6f7efcSMilanka Ringwald                 &handle_report_event, client->con_handle,
79983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
80083d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
80183d7ed1cSMilanka Ringwald             UNUSED(att_status);
80283d7ed1cSMilanka Ringwald             break;
80383d7ed1cSMilanka Ringwald 
80483d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
80583d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
806f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
807f4d3b82aSMilanka Ringwald                 client->report_index,
80883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
80983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
81083d7ed1cSMilanka Ringwald #endif
81183d7ed1cSMilanka Ringwald 
81283d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
813f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
81483d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
81583d7ed1cSMilanka Ringwald                 &handle_report_event,
81683d7ed1cSMilanka Ringwald                 client->con_handle,
81783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
81883d7ed1cSMilanka Ringwald             UNUSED(att_status);
81983d7ed1cSMilanka Ringwald             break;
82083d7ed1cSMilanka Ringwald 
82183d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
82283d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
82383d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
82483d7ed1cSMilanka Ringwald 
825f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
82683d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
82783d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
82883d7ed1cSMilanka Ringwald                 client->con_handle,
82983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
83083d7ed1cSMilanka Ringwald 
83183d7ed1cSMilanka Ringwald             break;
83283d7ed1cSMilanka Ringwald #endif
833af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
834af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
83583d7ed1cSMilanka Ringwald 
836f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
837f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
838f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
839f4d3b82aSMilanka Ringwald                 client->con_handle,
840af2241c2SMilanka Ringwald                 client->handle);
841f4d3b82aSMilanka Ringwald             break;
842*6d6f7efcSMilanka Ringwald 
843*6d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
844*6d6f7efcSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
845*6d6f7efcSMilanka Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
846*6d6f7efcSMilanka Ringwald #endif
847*6d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
848*6d6f7efcSMilanka Ringwald 
849*6d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
850*6d6f7efcSMilanka Ringwald             UNUSED(att_status);
851*6d6f7efcSMilanka Ringwald             break;
852*6d6f7efcSMilanka Ringwald 
8536bcfb631SMilanka Ringwald         default:
8546bcfb631SMilanka Ringwald             break;
8556bcfb631SMilanka Ringwald     }
8566bcfb631SMilanka Ringwald }
8576bcfb631SMilanka Ringwald 
858cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
859cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
860cf26c8fbSMilanka Ringwald     UNUSED(channel);
861cf26c8fbSMilanka Ringwald     UNUSED(size);
862cf26c8fbSMilanka Ringwald 
8636bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
864cf26c8fbSMilanka Ringwald     uint8_t att_status;
8656bcfb631SMilanka Ringwald     gatt_client_service_t service;
8666bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
86770093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
868ab116b1cSMilanka Ringwald 
869021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
870021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
871e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
8723322b222SMilanka Ringwald     uint8_t i;
8733322b222SMilanka Ringwald     uint8_t report_index;
874da142a6fSMilanka Ringwald 
875f4d3b82aSMilanka Ringwald     const uint8_t * value;
876f4d3b82aSMilanka Ringwald     uint16_t value_len;
877cf26c8fbSMilanka Ringwald 
878cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
879cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
880cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
881cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
882cf26c8fbSMilanka Ringwald 
8836bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
8846bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
8856bcfb631SMilanka Ringwald                 hids_finalize_client(client);
8866bcfb631SMilanka Ringwald                 break;
8876bcfb631SMilanka Ringwald             }
8886bcfb631SMilanka Ringwald 
889021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
890021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
8916bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
892021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
893021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
8946bcfb631SMilanka Ringwald                 client->num_instances++;
895021192e1SMilanka Ringwald 
896708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
897708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
898708c69d2SMilanka Ringwald #endif
899021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
900021192e1SMilanka Ringwald             }  else {
901021192e1SMilanka 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);
902da142a6fSMilanka Ringwald             }
9036bcfb631SMilanka Ringwald             break;
9046bcfb631SMilanka Ringwald 
9056bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9066bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
9076bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
9086bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
90919146789SMilanka Ringwald 
91019146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
91119146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
91219146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
91319146789SMilanka Ringwald                     break;
91419146789SMilanka Ringwald                 }
91519146789SMilanka Ringwald             }
91619146789SMilanka Ringwald 
91719146789SMilanka Ringwald             switch (characteristic.uuid16){
91819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
91919146789SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
920af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
92119146789SMilanka Ringwald                     break;
92219146789SMilanka Ringwald 
92319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
92419146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
92519146789SMilanka Ringwald                     break;
92619146789SMilanka Ringwald 
92719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
92819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
92919146789SMilanka Ringwald                     break;
93019146789SMilanka Ringwald 
93119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
932f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
93319146789SMilanka Ringwald                     break;
93419146789SMilanka Ringwald 
93519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
93619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
93719146789SMilanka Ringwald                     break;
93819146789SMilanka Ringwald 
93919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94019146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
94119146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
94219146789SMilanka Ringwald                     break;
94319146789SMilanka Ringwald 
94419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
945f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
94619146789SMilanka Ringwald                     break;
94719146789SMilanka Ringwald 
94819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
949f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
95019146789SMilanka Ringwald                     break;
95119146789SMilanka Ringwald 
95219146789SMilanka Ringwald                 default:
95319146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
95419146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
95519146789SMilanka Ringwald #endif
95619146789SMilanka Ringwald                     return;
95719146789SMilanka Ringwald             }
95819146789SMilanka Ringwald 
95919146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
96019146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
96119146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
96219146789SMilanka Ringwald                 characteristic.start_handle,
96319146789SMilanka Ringwald                 characteristic.properties,
96419146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
96519146789SMilanka Ringwald                 client->service_index);
96619146789SMilanka Ringwald 
96719146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
96819146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
96919146789SMilanka Ringwald             }
97019146789SMilanka Ringwald             printf("\n");
97119146789SMilanka Ringwald #endif
972cf26c8fbSMilanka Ringwald             break;
973cf26c8fbSMilanka Ringwald 
9748cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
975e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
9768cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
977e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
978da142a6fSMilanka Ringwald 
979f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
980f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
9818cec2b74SMilanka Ringwald 
982556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9838cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
984f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
985556456ccSMilanka Ringwald #endif
986f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
987f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
988da142a6fSMilanka Ringwald                 if (!stored){
989da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
990da142a6fSMilanka Ringwald                     break;
991da142a6fSMilanka Ringwald                 }
992da142a6fSMilanka Ringwald             }
993e7bd2dbeSMilanka Ringwald             break;
994e7bd2dbeSMilanka Ringwald 
99570093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
99670093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
99770093cf5SMilanka Ringwald             btstack_assert(client != NULL);
99870093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
99970093cf5SMilanka Ringwald 
1000e7bd2dbeSMilanka Ringwald             switch (client->state) {
1001e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1002e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1003e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
100419146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10053322b222SMilanka Ringwald 
1006556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1007556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1008556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1009556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1010556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1011556456ccSMilanka Ringwald                                 client->service_index, report_index);
1012556456ccSMilanka Ringwald                         }
1013556456ccSMilanka Ringwald #endif
1014e7bd2dbeSMilanka Ringwald                     }
1015e7bd2dbeSMilanka Ringwald                     break;
101628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
101770093cf5SMilanka Ringwald                     // setup for descriptor value query
101870093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1019af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1020556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1021556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1022556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1023556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
102419146789SMilanka Ringwald #endif
1025556456ccSMilanka Ringwald                     }
1026556456ccSMilanka Ringwald 
102719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1028556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
102983d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1030556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1031556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1032556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1033556456ccSMilanka Ringwald                     }
1034556456ccSMilanka Ringwald #endif
103570093cf5SMilanka Ringwald                     break;
1036556456ccSMilanka Ringwald 
1037e7bd2dbeSMilanka Ringwald                 default:
1038e7bd2dbeSMilanka Ringwald                     break;
1039e7bd2dbeSMilanka Ringwald             }
1040e7bd2dbeSMilanka Ringwald             break;
104170093cf5SMilanka Ringwald 
104283d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
104383d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
104483d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
104583d7ed1cSMilanka Ringwald 
1046f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1047f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1048f4d3b82aSMilanka Ringwald 
1049af2241c2SMilanka Ringwald 
1050f4d3b82aSMilanka Ringwald             switch (client->state){
1051f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1052f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
105383d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1054f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
105583d7ed1cSMilanka Ringwald                     break;
105683d7ed1cSMilanka Ringwald #endif
1057af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1058af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1059af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1060f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1061f4d3b82aSMilanka Ringwald                         break;
1062af2241c2SMilanka Ringwald                     }
1063af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1064af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1065af2241c2SMilanka Ringwald                         break;
1066af2241c2SMilanka Ringwald                     }
1067af2241c2SMilanka Ringwald                     break;
1068af2241c2SMilanka Ringwald                 }
1069f4d3b82aSMilanka Ringwald                 default:
1070f4d3b82aSMilanka Ringwald                     break;
1071f4d3b82aSMilanka Ringwald             }
1072f4d3b82aSMilanka Ringwald 
1073f4d3b82aSMilanka Ringwald             break;
107483d7ed1cSMilanka Ringwald 
107570093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
107670093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
107770093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1078e7bd2dbeSMilanka Ringwald 
1079e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1080e7bd2dbeSMilanka Ringwald                 break;
1081e7bd2dbeSMilanka Ringwald             }
10827e1e6e7dSMilanka Ringwald 
1083e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1084e7bd2dbeSMilanka Ringwald             switch (client->state) {
108528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1086e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
108719146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
10883322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
108919146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1090556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
109119146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
109219146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1093556456ccSMilanka Ringwald #endif
10943322b222SMilanka Ringwald                     }
1095e7bd2dbeSMilanka Ringwald                     break;
1096e7bd2dbeSMilanka Ringwald 
109728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
109819146789SMilanka Ringwald 
109919146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1100021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1101021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
110219146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
110383d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1104021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1105021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
110683d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
110719146789SMilanka Ringwald     #endif
110819146789SMilanka Ringwald                     }
1109e7bd2dbeSMilanka Ringwald                     break;
1110e7bd2dbeSMilanka Ringwald 
1111e7bd2dbeSMilanka Ringwald                 default:
1112e7bd2dbeSMilanka Ringwald                     break;
111370093cf5SMilanka Ringwald             }
111470093cf5SMilanka Ringwald             break;
111570093cf5SMilanka Ringwald 
1116cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1117cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1118cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1119cf26c8fbSMilanka Ringwald 
1120cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1121cf26c8fbSMilanka Ringwald 
1122cf26c8fbSMilanka Ringwald             switch (client->state){
1123cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
11246bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11256bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11266bcfb631SMilanka Ringwald                         hids_finalize_client(client);
1127cf26c8fbSMilanka Ringwald                         break;
11286bcfb631SMilanka Ringwald                     }
11296bcfb631SMilanka Ringwald 
11306bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
11316bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
11326bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11336bcfb631SMilanka Ringwald                         break;
11346bcfb631SMilanka Ringwald                     }
11356bcfb631SMilanka Ringwald 
11366bcfb631SMilanka Ringwald                     client->service_index = 0;
113719146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
11386bcfb631SMilanka Ringwald                     break;
11396bcfb631SMilanka Ringwald 
11406bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
11416bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11426bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11436bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11446bcfb631SMilanka Ringwald                         break;
11456bcfb631SMilanka Ringwald                     }
11466bcfb631SMilanka Ringwald 
114770093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
114870093cf5SMilanka Ringwald                         // discover characteristics of next service
114970093cf5SMilanka Ringwald                         client->service_index++;
115070093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1151021192e1SMilanka Ringwald                         break;
1152021192e1SMilanka Ringwald                     }
1153021192e1SMilanka Ringwald 
1154021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1155021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1156021192e1SMilanka Ringwald                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1157021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1158021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1159021192e1SMilanka Ringwald                                 break;
1160021192e1SMilanka Ringwald                             }
1161021192e1SMilanka Ringwald                             hids_emit_connection_established(client, att_status);
1162021192e1SMilanka Ringwald                             hids_finalize_client(client);
1163021192e1SMilanka Ringwald                             return;
1164021192e1SMilanka Ringwald 
1165021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
1166021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1167021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1168021192e1SMilanka Ringwald                                 break;
1169021192e1SMilanka Ringwald                             }
1170021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1171021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1172021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1173021192e1SMilanka Ringwald                                     break;
1174021192e1SMilanka Ringwald                                 }
1175021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1176021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1177021192e1SMilanka Ringwald                                 return;
1178021192e1SMilanka Ringwald                             }
1179021192e1SMilanka Ringwald                             break;
1180021192e1SMilanka Ringwald                         default:
1181021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1182021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1183021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1184021192e1SMilanka Ringwald                                     break;
1185021192e1SMilanka Ringwald                                 }
1186021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1187021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1188021192e1SMilanka Ringwald                                 return;
1189021192e1SMilanka Ringwald                             }
1190021192e1SMilanka Ringwald                             break;
1191021192e1SMilanka Ringwald                     }
1192021192e1SMilanka Ringwald 
1193021192e1SMilanka Ringwald                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1194021192e1SMilanka Ringwald                         break;
1195021192e1SMilanka Ringwald                     }
11963322b222SMilanka Ringwald 
11977e1e6e7dSMilanka Ringwald                     // 1. we need to get HID Descriptor and
11987e1e6e7dSMilanka Ringwald                     // 2. get external Report characteristics if referenced from Report Map
11997e1e6e7dSMilanka Ringwald                     if (hids_client_report_map_query_init(client)){
120070093cf5SMilanka Ringwald                         break;
120170093cf5SMilanka Ringwald                     }
120270093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
120370093cf5SMilanka Ringwald                     hids_finalize_client(client);
12046bcfb631SMilanka Ringwald                     break;
12056bcfb631SMilanka Ringwald 
12063322b222SMilanka Ringwald 
120728da36a6SMilanka Ringwald                 // HID descriptor found
120828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1209e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1210e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1211e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1212e7bd2dbeSMilanka Ringwald                         break;
1213e7bd2dbeSMilanka Ringwald                     }
1214e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1215e7bd2dbeSMilanka Ringwald                     break;
1216e7bd2dbeSMilanka Ringwald 
121728da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1218e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12193322b222SMilanka Ringwald                     // go for next report map
12203322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1221e7bd2dbeSMilanka Ringwald                         break;
1222e7bd2dbeSMilanka Ringwald                     }
1223e7bd2dbeSMilanka Ringwald 
12243322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12253322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1226e7bd2dbeSMilanka Ringwald                         break;
1227e7bd2dbeSMilanka Ringwald                     }
1228e7bd2dbeSMilanka Ringwald 
1229e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1230e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12317e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1232e7bd2dbeSMilanka Ringwald                         break;
1233e7bd2dbeSMilanka Ringwald                     }
1234e7bd2dbeSMilanka Ringwald 
1235e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1236e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1237e7bd2dbeSMilanka Ringwald                     break;
1238e7bd2dbeSMilanka Ringwald 
123928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1240e7bd2dbeSMilanka Ringwald                     // go for next map report
12413322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1242e7bd2dbeSMilanka Ringwald                         break;
1243e7bd2dbeSMilanka Ringwald                     }
1244e7bd2dbeSMilanka Ringwald 
12453322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12463322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1247e7bd2dbeSMilanka Ringwald                     break;
1248e7bd2dbeSMilanka Ringwald 
12493322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1250e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1251e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12527e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1253e7bd2dbeSMilanka Ringwald                         break;
1254e7bd2dbeSMilanka Ringwald                     }
1255e7bd2dbeSMilanka Ringwald 
1256e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1257e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1258e7bd2dbeSMilanka Ringwald                     break;
1259e7bd2dbeSMilanka Ringwald 
126028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1261af2241c2SMilanka Ringwald                     if (client->handle != 0){
126228da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
126370093cf5SMilanka Ringwald                         break;
126470093cf5SMilanka Ringwald                     }
126570093cf5SMilanka Ringwald 
126670093cf5SMilanka Ringwald                     // go for next report
12677e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
126870093cf5SMilanka Ringwald                         break;
126970093cf5SMilanka Ringwald                     }
1270835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
127170093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
127270093cf5SMilanka Ringwald                     break;
127370093cf5SMilanka Ringwald 
127428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
127570093cf5SMilanka Ringwald                     // go for next report
12767e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
127770093cf5SMilanka Ringwald                         break;
127870093cf5SMilanka Ringwald                     }
1279021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1280021192e1SMilanka Ringwald                         break;
1281021192e1SMilanka Ringwald                     }
1282835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
128370093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
128470093cf5SMilanka Ringwald                     break;
128570093cf5SMilanka Ringwald 
1286021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1287021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
12882901a9b7SMilanka Ringwald                         break;
12892901a9b7SMilanka Ringwald                     }
1290835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
12916bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
12926bcfb631SMilanka Ringwald                     break;
1293f4d3b82aSMilanka Ringwald 
1294835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1295835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1296835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1297835a13f1SMilanka Ringwald                     break;
1298835a13f1SMilanka Ringwald #endif
1299f4d3b82aSMilanka Ringwald 
1300af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1301*6d6f7efcSMilanka Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1302f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1303f4d3b82aSMilanka Ringwald                     break;
1304f4d3b82aSMilanka Ringwald 
1305*6d6f7efcSMilanka Ringwald 
1306cf26c8fbSMilanka Ringwald                 default:
1307cf26c8fbSMilanka Ringwald                     break;
1308cf26c8fbSMilanka Ringwald             }
1309cf26c8fbSMilanka Ringwald             break;
1310cf26c8fbSMilanka Ringwald 
1311cf26c8fbSMilanka Ringwald         default:
1312cf26c8fbSMilanka Ringwald             break;
1313cf26c8fbSMilanka Ringwald     }
13146bcfb631SMilanka Ringwald 
13156bcfb631SMilanka Ringwald     if (client != NULL){
13166bcfb631SMilanka Ringwald         hids_run_for_client(client);
13176bcfb631SMilanka Ringwald     }
1318cf26c8fbSMilanka Ringwald }
1319cf26c8fbSMilanka Ringwald 
13206bcfb631SMilanka 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){
1321cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1322cf26c8fbSMilanka Ringwald 
1323cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1324cf26c8fbSMilanka Ringwald     if (client != NULL){
1325cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1326cf26c8fbSMilanka Ringwald     }
1327cf26c8fbSMilanka Ringwald 
1328cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1329cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1330cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1331cf26c8fbSMilanka Ringwald     }
1332cf26c8fbSMilanka Ringwald 
1333cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1334cf26c8fbSMilanka Ringwald     if (client == NULL) {
1335cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1336cf26c8fbSMilanka Ringwald     }
1337cf26c8fbSMilanka Ringwald 
13386bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1339cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1340cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1341cf26c8fbSMilanka Ringwald 
1342cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1343cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1344cf26c8fbSMilanka Ringwald }
1345cf26c8fbSMilanka Ringwald 
1346cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1347cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1348cf26c8fbSMilanka Ringwald     if (client == NULL){
1349cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1350cf26c8fbSMilanka Ringwald     }
1351cf26c8fbSMilanka Ringwald     // finalize connection
1352cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1353cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1354cf26c8fbSMilanka Ringwald }
1355cf26c8fbSMilanka Ringwald 
1356*6d6f7efcSMilanka Ringwald uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
13571624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
13581624214bSMilanka Ringwald     if (client == NULL){
13591624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
13601624214bSMilanka Ringwald     }
13611624214bSMilanka Ringwald 
13621624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
13631624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
13641624214bSMilanka Ringwald     }
13651624214bSMilanka Ringwald 
1366*6d6f7efcSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id(client, report_id);
136783d7ed1cSMilanka Ringwald 
13681624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
13691624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
13701624214bSMilanka Ringwald     }
13711624214bSMilanka Ringwald 
13721624214bSMilanka Ringwald     uint16_t mtu;
13731624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
13741624214bSMilanka Ringwald 
13751624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
13761624214bSMilanka Ringwald         return status;
13771624214bSMilanka Ringwald     }
13781624214bSMilanka Ringwald 
13791624214bSMilanka Ringwald     if (mtu - 2 < report_len){
13801624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
13811624214bSMilanka Ringwald     }
13821624214bSMilanka Ringwald 
1383*6d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1384021192e1SMilanka Ringwald     client->report_index = report_index;
13851624214bSMilanka Ringwald     client->report = report;
13861624214bSMilanka Ringwald     client->report_len = report_len;
13871624214bSMilanka Ringwald 
13881624214bSMilanka Ringwald     hids_run_for_client(client);
13891624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
13901624214bSMilanka Ringwald }
13911624214bSMilanka Ringwald 
139283d7ed1cSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id){
139383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
139483d7ed1cSMilanka Ringwald     if (client == NULL){
139583d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
139683d7ed1cSMilanka Ringwald     }
139783d7ed1cSMilanka Ringwald 
139883d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
139983d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
140083d7ed1cSMilanka Ringwald     }
140183d7ed1cSMilanka Ringwald 
1402835a13f1SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id(client, report_id);
140383d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
140483d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
140583d7ed1cSMilanka Ringwald     }
140683d7ed1cSMilanka Ringwald 
140783d7ed1cSMilanka Ringwald     client->report_index = report_index;
140883d7ed1cSMilanka Ringwald 
140983d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
141083d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
141183d7ed1cSMilanka Ringwald #else
141283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
141383d7ed1cSMilanka Ringwald #endif
141483d7ed1cSMilanka Ringwald     hids_run_for_client(client);
141583d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
141683d7ed1cSMilanka Ringwald }
141783d7ed1cSMilanka Ringwald 
141883d7ed1cSMilanka Ringwald 
1419f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1420f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1421f4d3b82aSMilanka Ringwald     if (client == NULL){
1422f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1423f4d3b82aSMilanka Ringwald     }
1424f4d3b82aSMilanka Ringwald 
1425f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1426f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1427f4d3b82aSMilanka Ringwald     }
1428f4d3b82aSMilanka Ringwald 
1429f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1430f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1431f4d3b82aSMilanka Ringwald     }
1432f4d3b82aSMilanka Ringwald 
1433f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1434af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1435af2241c2SMilanka Ringwald 
1436af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1437af2241c2SMilanka Ringwald     hids_run_for_client(client);
1438af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1439af2241c2SMilanka Ringwald }
1440af2241c2SMilanka Ringwald 
1441af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1442af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1443af2241c2SMilanka Ringwald     if (client == NULL){
1444af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1445af2241c2SMilanka Ringwald     }
1446af2241c2SMilanka Ringwald 
1447af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1448af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1449af2241c2SMilanka Ringwald     }
1450af2241c2SMilanka Ringwald 
1451af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1452af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1453af2241c2SMilanka Ringwald     }
1454af2241c2SMilanka Ringwald 
1455af2241c2SMilanka Ringwald     client->service_index = service_index;
1456af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1457af2241c2SMilanka Ringwald 
1458af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1459f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1460f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1461f4d3b82aSMilanka Ringwald }
1462f4d3b82aSMilanka Ringwald 
1463*6d6f7efcSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, hid_protocol_mode_t protocol_mode, uint8_t service_index){
1464*6d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1465*6d6f7efcSMilanka Ringwald     if (client == NULL){
1466*6d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1467*6d6f7efcSMilanka Ringwald     }
1468*6d6f7efcSMilanka Ringwald 
1469*6d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1470*6d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1471*6d6f7efcSMilanka Ringwald     }
1472*6d6f7efcSMilanka Ringwald 
1473*6d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
1474*6d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1475*6d6f7efcSMilanka Ringwald     }
1476*6d6f7efcSMilanka Ringwald 
1477*6d6f7efcSMilanka Ringwald     client->service_index = service_index;
1478*6d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1479*6d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
1480*6d6f7efcSMilanka Ringwald 
1481*6d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1482*6d6f7efcSMilanka Ringwald     hids_run_for_client(client);
1483*6d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1484*6d6f7efcSMilanka Ringwald }
1485*6d6f7efcSMilanka Ringwald 
1486*6d6f7efcSMilanka Ringwald 
1487*6d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
1488*6d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1489*6d6f7efcSMilanka Ringwald     if (client == NULL){
1490*6d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1491*6d6f7efcSMilanka Ringwald     }
1492*6d6f7efcSMilanka Ringwald 
1493*6d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1494*6d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1495*6d6f7efcSMilanka Ringwald     }
1496*6d6f7efcSMilanka Ringwald 
1497*6d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
1498*6d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1499*6d6f7efcSMilanka Ringwald     }
1500*6d6f7efcSMilanka Ringwald 
1501*6d6f7efcSMilanka Ringwald     client->service_index = service_index;
1502*6d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
1503*6d6f7efcSMilanka Ringwald     client->value = value;
1504*6d6f7efcSMilanka Ringwald 
1505*6d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1506*6d6f7efcSMilanka Ringwald     hids_run_for_client(client);
1507*6d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1508*6d6f7efcSMilanka Ringwald }
1509*6d6f7efcSMilanka Ringwald 
1510*6d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
1511*6d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
1512*6d6f7efcSMilanka Ringwald }
1513*6d6f7efcSMilanka Ringwald 
1514*6d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
1515*6d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
1516*6d6f7efcSMilanka Ringwald }
1517*6d6f7efcSMilanka Ringwald 
1518*6d6f7efcSMilanka Ringwald 
1519021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1520021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1521021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1522021192e1SMilanka Ringwald }
1523cf26c8fbSMilanka Ringwald 
1524cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1525