xref: /btstack/src/ble/gatt-service/hids_client.c (revision fd39e93aa91ae0290e95abf4d559cf9383682760)
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 
253*fd39e93aSMilanka Ringwald static uint8_t find_report_index_for_report_id_and_report_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
25483d7ed1cSMilanka Ringwald     uint8_t i;
255835a13f1SMilanka Ringwald 
25683d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
257*fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
258*fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
259*fd39e93aSMilanka Ringwald 
260*fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
26183d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
26283d7ed1cSMilanka Ringwald                 continue;
26383d7ed1cSMilanka Ringwald             }
264*fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
26583d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
26683d7ed1cSMilanka Ringwald                 continue;
26783d7ed1cSMilanka Ringwald             }
268*fd39e93aSMilanka Ringwald         }
269*fd39e93aSMilanka Ringwald 
270*fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
27183d7ed1cSMilanka Ringwald             return i;
27283d7ed1cSMilanka Ringwald         }
27383d7ed1cSMilanka Ringwald     }
27483d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
27583d7ed1cSMilanka Ringwald }
27619146789SMilanka Ringwald 
277021192e1SMilanka 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){
278ab116b1cSMilanka Ringwald 
27919146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
280ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2813322b222SMilanka Ringwald         return report_index;
282ab116b1cSMilanka Ringwald     }
28328da36a6SMilanka Ringwald     report_index = client->num_reports;
284ab116b1cSMilanka Ringwald 
28528da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
28628da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
28728da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
28828da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
289ab116b1cSMilanka Ringwald 
29028da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
29128da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
29228da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
293021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
29470093cf5SMilanka Ringwald 
295021192e1SMilanka 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);
296ab116b1cSMilanka Ringwald         client->num_reports++;
2973322b222SMilanka Ringwald         return report_index;
298ab116b1cSMilanka Ringwald     } else {
299ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3003322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
301ab116b1cSMilanka Ringwald     }
302ab116b1cSMilanka Ringwald }
303ab116b1cSMilanka Ringwald 
30419146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
305556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
306556456ccSMilanka Ringwald 
307556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
30819146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
309556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
31019146789SMilanka Ringwald 
311556456ccSMilanka Ringwald         client->num_external_reports++;
31219146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
313556456ccSMilanka Ringwald         return report_index;
314556456ccSMilanka Ringwald     } else {
315556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
316556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
31770093cf5SMilanka Ringwald     }
318556456ccSMilanka Ringwald }
319556456ccSMilanka Ringwald 
3207e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3217e1e6e7dSMilanka Ringwald     uint8_t i;
3223322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3233322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
324556456ccSMilanka Ringwald             return i;
3253322b222SMilanka Ringwald         }
3263322b222SMilanka Ringwald     }
327556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
328556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3293322b222SMilanka Ringwald }
3303322b222SMilanka Ringwald 
3313322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3323322b222SMilanka Ringwald     client->service_index++;
3333322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
334556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3353322b222SMilanka Ringwald         return true;
3363322b222SMilanka Ringwald     }
3373322b222SMilanka Ringwald     return false;
3383322b222SMilanka Ringwald }
3393322b222SMilanka Ringwald 
3403322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3413322b222SMilanka Ringwald     client->service_index = 0;
3423322b222SMilanka Ringwald 
3433322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3443322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3453322b222SMilanka Ringwald         return true;
3463322b222SMilanka Ringwald     }
3473322b222SMilanka Ringwald     return false;
3483322b222SMilanka Ringwald }
3493322b222SMilanka Ringwald 
3503322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
351021192e1SMilanka Ringwald     client->report_index++;
35219146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3533322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
354e7bd2dbeSMilanka Ringwald         return true;
355e7bd2dbeSMilanka Ringwald     }
356e7bd2dbeSMilanka Ringwald     return false;
357e7bd2dbeSMilanka Ringwald }
358e7bd2dbeSMilanka Ringwald 
3593322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
360021192e1SMilanka Ringwald     client->report_index = 0;
36119146789SMilanka Ringwald     if (client->num_external_reports > 0){
3623322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3637e1e6e7dSMilanka Ringwald         return true;
3647e1e6e7dSMilanka Ringwald     }
3657e1e6e7dSMilanka Ringwald     return false;
3667e1e6e7dSMilanka Ringwald }
3677e1e6e7dSMilanka Ringwald 
3687e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3697e1e6e7dSMilanka Ringwald     uint8_t i;
3707e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
371*fd39e93aSMilanka Ringwald 
372021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
37370093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
374*fd39e93aSMilanka Ringwald         if (!report.boot_report){
37570093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3767e1e6e7dSMilanka Ringwald                 index = i;
377021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3787e1e6e7dSMilanka Ringwald                 break;
37970093cf5SMilanka Ringwald             }
380*fd39e93aSMilanka Ringwald         }
381*fd39e93aSMilanka Ringwald     }
382021192e1SMilanka Ringwald     client->report_index = index;
3837e1e6e7dSMilanka Ringwald     return index;
3847e1e6e7dSMilanka Ringwald }
3857e1e6e7dSMilanka Ringwald 
3867e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
387021192e1SMilanka Ringwald     client->report_index++;
3887e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
38928da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
390e7bd2dbeSMilanka Ringwald         return true;
39170093cf5SMilanka Ringwald     }
3927e1e6e7dSMilanka Ringwald     return false;
3937e1e6e7dSMilanka Ringwald }
3947e1e6e7dSMilanka Ringwald 
3957e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
396021192e1SMilanka Ringwald     client->report_index = 0;
3977e1e6e7dSMilanka Ringwald 
3987e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
39928da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4007e1e6e7dSMilanka Ringwald         return true;
4017e1e6e7dSMilanka Ringwald     }
402e7bd2dbeSMilanka Ringwald     return false;
40370093cf5SMilanka Ringwald }
404ab116b1cSMilanka Ringwald 
405021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
406021192e1SMilanka Ringwald     uint8_t i;
407021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
408021192e1SMilanka Ringwald 
409021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
410021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
411*fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
412*fd39e93aSMilanka Ringwald 
413*fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
414*fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
415021192e1SMilanka Ringwald                 continue;
416021192e1SMilanka Ringwald             }
417*fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
418*fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
419*fd39e93aSMilanka Ringwald                 continue;
420*fd39e93aSMilanka Ringwald             }
421*fd39e93aSMilanka Ringwald         }
422*fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
423021192e1SMilanka Ringwald             index = i;
424021192e1SMilanka Ringwald         }
425021192e1SMilanka Ringwald     }
426021192e1SMilanka Ringwald     client->report_index = index;
427021192e1SMilanka Ringwald     return index;
428021192e1SMilanka Ringwald }
429021192e1SMilanka Ringwald 
430021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
431021192e1SMilanka Ringwald     client->report_index++;
432021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
433021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
434021192e1SMilanka Ringwald         return true;
435021192e1SMilanka Ringwald     }
436021192e1SMilanka Ringwald     return false;
437021192e1SMilanka Ringwald }
438021192e1SMilanka Ringwald 
439021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
440cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
441cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
442cd28e7b1SMilanka Ringwald #endif
443021192e1SMilanka Ringwald     client->report_index = 0;
444021192e1SMilanka Ringwald 
445021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
446021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
447021192e1SMilanka Ringwald         return true;
448021192e1SMilanka Ringwald     }
449021192e1SMilanka Ringwald     return false;
450021192e1SMilanka Ringwald }
451021192e1SMilanka Ringwald 
452cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
453cd28e7b1SMilanka Ringwald     client->report_index++;
454cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
455cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
456cd28e7b1SMilanka Ringwald         return true;
457cd28e7b1SMilanka Ringwald     }
458cd28e7b1SMilanka Ringwald     return false;
459cd28e7b1SMilanka Ringwald }
460cd28e7b1SMilanka Ringwald 
461cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
462cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
463cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
464cd28e7b1SMilanka Ringwald #endif
465cd28e7b1SMilanka Ringwald     client->report_index = 0;
466cd28e7b1SMilanka Ringwald 
467cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
468cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
469cd28e7b1SMilanka Ringwald         return true;
470cd28e7b1SMilanka Ringwald     }
471cd28e7b1SMilanka Ringwald     return false;
472cd28e7b1SMilanka Ringwald }
473cd28e7b1SMilanka Ringwald 
474cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
475cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
476cf26c8fbSMilanka Ringwald     if (!client){
477cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
478cf26c8fbSMilanka Ringwald         return NULL;
479cf26c8fbSMilanka Ringwald     }
480cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
481cf26c8fbSMilanka Ringwald     client->cid = cid;
482cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
483fc975d0eSMilanka Ringwald 
484cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
485cf26c8fbSMilanka Ringwald     return client;
486fc975d0eSMilanka Ringwald }
487fc975d0eSMilanka Ringwald 
488cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
489021192e1SMilanka Ringwald     // stop listening
490021192e1SMilanka Ringwald     uint8_t i;
491021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
492021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
493021192e1SMilanka Ringwald     }
494021192e1SMilanka Ringwald 
495da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
496cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
497cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
498fc975d0eSMilanka Ringwald }
499cf26c8fbSMilanka Ringwald 
500cf26c8fbSMilanka Ringwald 
501cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5026bcfb631SMilanka Ringwald     uint8_t event[8];
503cf26c8fbSMilanka Ringwald     int pos = 0;
504cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
505cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
506cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
507cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
508cf26c8fbSMilanka Ringwald     pos += 2;
509cf26c8fbSMilanka Ringwald     event[pos++] = status;
510*fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
511cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
512cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
513cf26c8fbSMilanka Ringwald }
514cf26c8fbSMilanka Ringwald 
515cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
516cd28e7b1SMilanka Ringwald     uint8_t event[6];
517cd28e7b1SMilanka Ringwald     int pos = 0;
518cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
519cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
520cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
521cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
522cd28e7b1SMilanka Ringwald     pos += 2;
523cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
524cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
525cd28e7b1SMilanka Ringwald }
526cd28e7b1SMilanka Ringwald 
527021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
528021192e1SMilanka Ringwald     uint16_t pos = 0;
529021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
530021192e1SMilanka Ringwald     pos++;  // skip len
531021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
532021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
533021192e1SMilanka Ringwald     pos += 2;
534021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
535021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
536021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
537021192e1SMilanka Ringwald     pos += 2;
538021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
53983d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
54083d7ed1cSMilanka Ringwald 
541021192e1SMilanka Ringwald }
542021192e1SMilanka Ringwald 
543f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
544f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
545f4d3b82aSMilanka Ringwald 
546f4d3b82aSMilanka Ringwald     uint8_t event[11];
547f4d3b82aSMilanka Ringwald     int pos = 0;
548f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
549f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
550f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
551f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
552f4d3b82aSMilanka Ringwald     pos += 2;
553f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
554f4d3b82aSMilanka Ringwald 
555f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
556f4d3b82aSMilanka Ringwald     pos += 3;
557f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
558f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
559f4d3b82aSMilanka Ringwald 
560f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
561f4d3b82aSMilanka Ringwald }
562f4d3b82aSMilanka Ringwald 
563af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
564af2241c2SMilanka Ringwald     if (value_len != 1) return;
565af2241c2SMilanka Ringwald 
566af2241c2SMilanka Ringwald     uint8_t event[11];
567af2241c2SMilanka Ringwald     int pos = 0;
568af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
569af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
570af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
571af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
572af2241c2SMilanka Ringwald     pos += 2;
573af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
574af2241c2SMilanka Ringwald     event[pos++] = value[0];
575af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
576af2241c2SMilanka Ringwald }
577f4d3b82aSMilanka Ringwald 
57883d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
579021192e1SMilanka Ringwald     UNUSED(packet_type);
580021192e1SMilanka Ringwald     UNUSED(channel);
58183d7ed1cSMilanka Ringwald 
582021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
583021192e1SMilanka Ringwald 
584021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
585021192e1SMilanka Ringwald     btstack_assert(client != NULL);
586021192e1SMilanka Ringwald 
587021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
588021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
589021192e1SMilanka Ringwald         return;
590021192e1SMilanka Ringwald     }
591021192e1SMilanka Ringwald 
59283d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
593021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
59483d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
595021192e1SMilanka Ringwald }
596021192e1SMilanka Ringwald 
59783d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
59883d7ed1cSMilanka Ringwald     UNUSED(packet_type);
59983d7ed1cSMilanka Ringwald     UNUSED(channel);
60083d7ed1cSMilanka Ringwald 
60183d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
60283d7ed1cSMilanka Ringwald 
60383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
60483d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
60583d7ed1cSMilanka Ringwald 
60683d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
60783d7ed1cSMilanka Ringwald         return;
60883d7ed1cSMilanka Ringwald     }
60983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
61083d7ed1cSMilanka Ringwald 
61183d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
61283d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
61383d7ed1cSMilanka Ringwald         return;
61483d7ed1cSMilanka Ringwald     }
61583d7ed1cSMilanka Ringwald 
61683d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
61783d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
61883d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
61983d7ed1cSMilanka Ringwald }
620cf26c8fbSMilanka Ringwald 
621cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
622cf26c8fbSMilanka Ringwald     uint8_t att_status;
6236bcfb631SMilanka Ringwald     gatt_client_service_t service;
6246bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
625cf26c8fbSMilanka Ringwald 
626cf26c8fbSMilanka Ringwald     switch (client->state){
627cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
628556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
629556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
630556456ccSMilanka Ringwald #endif
631cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
63219146789SMilanka Ringwald 
63319146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
634cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
635cf26c8fbSMilanka Ringwald             UNUSED(att_status);
636cf26c8fbSMilanka Ringwald             break;
637cf26c8fbSMilanka Ringwald 
638cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
639556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
640556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
641556456ccSMilanka Ringwald #endif
6426bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6436bcfb631SMilanka Ringwald 
6446bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6456bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
64619146789SMilanka Ringwald 
64719146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6486bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6496bcfb631SMilanka Ringwald 
6506bcfb631SMilanka Ringwald             UNUSED(att_status);
6516bcfb631SMilanka Ringwald             break;
6526bcfb631SMilanka Ringwald 
65328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
654556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
655556456ccSMilanka 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);
656556456ccSMilanka Ringwald #endif
65728da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
658556456ccSMilanka Ringwald 
65919146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
66019146789SMilanka 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);
661e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
662e7bd2dbeSMilanka Ringwald             break;
663e7bd2dbeSMilanka Ringwald 
664e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
665556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
666556456ccSMilanka 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);
667556456ccSMilanka Ringwald #endif
668e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
669e7bd2dbeSMilanka Ringwald 
6703322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6713322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6723322b222SMilanka Ringwald 
67319146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
674e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
675e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
676e7bd2dbeSMilanka Ringwald             break;
677e7bd2dbeSMilanka Ringwald 
67828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
679556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
68019146789SMilanka 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);
681556456ccSMilanka Ringwald #endif
68228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
683e7bd2dbeSMilanka Ringwald 
68419146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
68519146789SMilanka 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);
686e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
687e7bd2dbeSMilanka Ringwald             break;
688e7bd2dbeSMilanka Ringwald 
6893322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
690556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
69119146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
692556456ccSMilanka Ringwald #endif
6933322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
694e7bd2dbeSMilanka Ringwald 
6953322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
6963322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
69719146789SMilanka Ringwald 
69819146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6993322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
700e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
701e7bd2dbeSMilanka Ringwald             break;
702e7bd2dbeSMilanka Ringwald 
70328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
704556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
70583d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
706556456ccSMilanka Ringwald                 client->report_index,
70783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
70883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
709556456ccSMilanka Ringwald #endif
71028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
711af2241c2SMilanka Ringwald             client->handle = 0;
712556456ccSMilanka Ringwald 
713556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
714556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
715556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
716556456ccSMilanka Ringwald 
71719146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
71870093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
71970093cf5SMilanka Ringwald             UNUSED(att_status);
72070093cf5SMilanka Ringwald             break;
72170093cf5SMilanka Ringwald 
72228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
72328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
72470093cf5SMilanka Ringwald 
72519146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
726af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
727af2241c2SMilanka Ringwald             client->handle = 0;
72870093cf5SMilanka Ringwald             UNUSED(att_status);
72970093cf5SMilanka Ringwald             break;
73070093cf5SMilanka Ringwald 
731cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
732cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
733cd28e7b1SMilanka Ringwald             if (client->value > 0){
734cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
735cd28e7b1SMilanka Ringwald             } else {
736cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
737cd28e7b1SMilanka Ringwald             }
738cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
739cd28e7b1SMilanka Ringwald                 client->report_index,
740cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
741cd28e7b1SMilanka Ringwald #endif
742cd28e7b1SMilanka Ringwald 
743cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
744cd28e7b1SMilanka Ringwald 
745cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
746cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
747cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
748cd28e7b1SMilanka Ringwald 
749cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
750cd28e7b1SMilanka Ringwald 
751cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
752cd28e7b1SMilanka Ringwald 
753cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
754cd28e7b1SMilanka Ringwald                 switch(client->value){
755cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
756cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
757cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
758cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
759cd28e7b1SMilanka Ringwald                         break;
760cd28e7b1SMilanka Ringwald                     default:
761cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
762cd28e7b1SMilanka Ringwald                         break;
763cd28e7b1SMilanka Ringwald                 }
764cd28e7b1SMilanka Ringwald             } else {
765cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
766cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
767cd28e7b1SMilanka Ringwald                     break;
768cd28e7b1SMilanka Ringwald                 }
769cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
770cd28e7b1SMilanka Ringwald             }
771cd28e7b1SMilanka Ringwald             break;
772cd28e7b1SMilanka Ringwald 
773021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
774cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
775cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
776cd28e7b1SMilanka Ringwald                 client->report_index,
777cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
778cd28e7b1SMilanka Ringwald #endif
779021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
780021192e1SMilanka Ringwald 
781021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
782021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
783021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
784021192e1SMilanka Ringwald 
78519146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
786021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
787021192e1SMilanka Ringwald 
788cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
789cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
790cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
791cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
792cd28e7b1SMilanka Ringwald             } else {
793021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
794021192e1SMilanka Ringwald                     hids_run_for_client(client);
795021192e1SMilanka Ringwald                     break;
796021192e1SMilanka Ringwald                 }
797021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
798021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
799021192e1SMilanka Ringwald             }
800021192e1SMilanka Ringwald             break;
801021192e1SMilanka Ringwald 
80283d7ed1cSMilanka Ringwald 
8036d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
80483d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8056d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
80683d7ed1cSMilanka Ringwald                 client->report_index,
80783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
80883d7ed1cSMilanka Ringwald #endif
80983d7ed1cSMilanka Ringwald 
8106d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
81183d7ed1cSMilanka Ringwald 
8126d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8136d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
8146d6f7efcSMilanka Ringwald                 &handle_report_event, client->con_handle,
81583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
81683d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
81783d7ed1cSMilanka Ringwald             UNUSED(att_status);
81883d7ed1cSMilanka Ringwald             break;
81983d7ed1cSMilanka Ringwald 
82083d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
82183d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
822f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
823f4d3b82aSMilanka Ringwald                 client->report_index,
82483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
82583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82683d7ed1cSMilanka Ringwald #endif
82783d7ed1cSMilanka Ringwald 
82883d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
829f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
83083d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
83183d7ed1cSMilanka Ringwald                 &handle_report_event,
83283d7ed1cSMilanka Ringwald                 client->con_handle,
83383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
83483d7ed1cSMilanka Ringwald             UNUSED(att_status);
83583d7ed1cSMilanka Ringwald             break;
83683d7ed1cSMilanka Ringwald 
83783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
83883d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
83983d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
84083d7ed1cSMilanka Ringwald 
841f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
84283d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
84383d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
84483d7ed1cSMilanka Ringwald                 client->con_handle,
84583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
84683d7ed1cSMilanka Ringwald 
84783d7ed1cSMilanka Ringwald             break;
84883d7ed1cSMilanka Ringwald #endif
849af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
850af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
85183d7ed1cSMilanka Ringwald 
852f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
853f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
854f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
855f4d3b82aSMilanka Ringwald                 client->con_handle,
856af2241c2SMilanka Ringwald                 client->handle);
857f4d3b82aSMilanka Ringwald             break;
8586d6f7efcSMilanka Ringwald 
859*fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8606d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
861*fd39e93aSMilanka Ringwald             (void) gatt_client_request_can_write_without_response_event(&handle_gatt_client_event, client->con_handle);
8626d6f7efcSMilanka Ringwald             break;
8636d6f7efcSMilanka Ringwald 
8646bcfb631SMilanka Ringwald         default:
8656bcfb631SMilanka Ringwald             break;
8666bcfb631SMilanka Ringwald     }
8676bcfb631SMilanka Ringwald }
8686bcfb631SMilanka Ringwald 
869cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
870cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
871cf26c8fbSMilanka Ringwald     UNUSED(channel);
872cf26c8fbSMilanka Ringwald     UNUSED(size);
873cf26c8fbSMilanka Ringwald 
8746bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
875cf26c8fbSMilanka Ringwald     uint8_t att_status;
8766bcfb631SMilanka Ringwald     gatt_client_service_t service;
8776bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
87870093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
879ab116b1cSMilanka Ringwald 
880021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
881021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
882e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
8833322b222SMilanka Ringwald     uint8_t i;
8843322b222SMilanka Ringwald     uint8_t report_index;
885da142a6fSMilanka Ringwald 
886f4d3b82aSMilanka Ringwald     const uint8_t * value;
887f4d3b82aSMilanka Ringwald     uint16_t value_len;
888cf26c8fbSMilanka Ringwald 
889cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
890cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
891cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
892cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
893cf26c8fbSMilanka Ringwald 
8946bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
8956bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
8966bcfb631SMilanka Ringwald                 hids_finalize_client(client);
8976bcfb631SMilanka Ringwald                 break;
8986bcfb631SMilanka Ringwald             }
8996bcfb631SMilanka Ringwald 
900021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
901021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9026bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
903021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
904021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9056bcfb631SMilanka Ringwald                 client->num_instances++;
906021192e1SMilanka Ringwald 
907708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
908708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
909708c69d2SMilanka Ringwald #endif
910021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
911021192e1SMilanka Ringwald             }  else {
912021192e1SMilanka 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);
913da142a6fSMilanka Ringwald             }
9146bcfb631SMilanka Ringwald             break;
9156bcfb631SMilanka Ringwald 
9166bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9176bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
9186bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
9196bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
92019146789SMilanka Ringwald 
92119146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
92219146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
92319146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
92419146789SMilanka Ringwald                     break;
92519146789SMilanka Ringwald                 }
92619146789SMilanka Ringwald             }
92719146789SMilanka Ringwald 
92819146789SMilanka Ringwald             switch (characteristic.uuid16){
92919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
930af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
93119146789SMilanka Ringwald                     break;
93219146789SMilanka Ringwald 
93319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
93419146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
93519146789SMilanka Ringwald                     break;
93619146789SMilanka Ringwald 
93719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
93819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
93919146789SMilanka Ringwald                     break;
94019146789SMilanka Ringwald 
94119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
942f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
94319146789SMilanka Ringwald                     break;
94419146789SMilanka Ringwald 
94519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
94619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
94719146789SMilanka Ringwald                     break;
94819146789SMilanka Ringwald 
94919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
95019146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
95119146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
95219146789SMilanka Ringwald                     break;
95319146789SMilanka Ringwald 
95419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
955f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
95619146789SMilanka Ringwald                     break;
95719146789SMilanka Ringwald 
95819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
959f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
96019146789SMilanka Ringwald                     break;
96119146789SMilanka Ringwald 
96219146789SMilanka Ringwald                 default:
96319146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
96419146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
96519146789SMilanka Ringwald #endif
96619146789SMilanka Ringwald                     return;
96719146789SMilanka Ringwald             }
96819146789SMilanka Ringwald 
96919146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
97019146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
97119146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
97219146789SMilanka Ringwald                 characteristic.start_handle,
97319146789SMilanka Ringwald                 characteristic.properties,
97419146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
97519146789SMilanka Ringwald                 client->service_index);
97619146789SMilanka Ringwald 
97719146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
97819146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
97919146789SMilanka Ringwald             }
98019146789SMilanka Ringwald             printf("\n");
98119146789SMilanka Ringwald #endif
982cf26c8fbSMilanka Ringwald             break;
983cf26c8fbSMilanka Ringwald 
9848cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
985e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
9868cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
987e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
988da142a6fSMilanka Ringwald 
989f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
990f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
9918cec2b74SMilanka Ringwald 
992556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9938cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
994f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
995556456ccSMilanka Ringwald #endif
996f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
997f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
998da142a6fSMilanka Ringwald                 if (!stored){
999da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1000da142a6fSMilanka Ringwald                     break;
1001da142a6fSMilanka Ringwald                 }
1002da142a6fSMilanka Ringwald             }
1003e7bd2dbeSMilanka Ringwald             break;
1004e7bd2dbeSMilanka Ringwald 
100570093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
100670093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
100770093cf5SMilanka Ringwald             btstack_assert(client != NULL);
100870093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
100970093cf5SMilanka Ringwald 
1010e7bd2dbeSMilanka Ringwald             switch (client->state) {
1011e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1012e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1013e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
101419146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10153322b222SMilanka Ringwald 
1016556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1017556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1018556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1019556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1020556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1021556456ccSMilanka Ringwald                                 client->service_index, report_index);
1022556456ccSMilanka Ringwald                         }
1023556456ccSMilanka Ringwald #endif
1024e7bd2dbeSMilanka Ringwald                     }
1025e7bd2dbeSMilanka Ringwald                     break;
102628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
102770093cf5SMilanka Ringwald                     // setup for descriptor value query
102870093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1029af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1030556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1031556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1032556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1033556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
103419146789SMilanka Ringwald #endif
1035556456ccSMilanka Ringwald                     }
1036556456ccSMilanka Ringwald 
103719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1038556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
103983d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1040556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1041556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1042556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1043556456ccSMilanka Ringwald                     }
1044556456ccSMilanka Ringwald #endif
104570093cf5SMilanka Ringwald                     break;
1046556456ccSMilanka Ringwald 
1047e7bd2dbeSMilanka Ringwald                 default:
1048e7bd2dbeSMilanka Ringwald                     break;
1049e7bd2dbeSMilanka Ringwald             }
1050e7bd2dbeSMilanka Ringwald             break;
105170093cf5SMilanka Ringwald 
105283d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
105383d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
105483d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
105583d7ed1cSMilanka Ringwald 
1056f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1057f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1058f4d3b82aSMilanka Ringwald 
1059af2241c2SMilanka Ringwald 
1060f4d3b82aSMilanka Ringwald             switch (client->state){
1061f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1062f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
106383d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1064f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
106583d7ed1cSMilanka Ringwald                     break;
106683d7ed1cSMilanka Ringwald #endif
1067af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1068af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1069af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1070f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1071f4d3b82aSMilanka Ringwald                         break;
1072af2241c2SMilanka Ringwald                     }
1073af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1074af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1075af2241c2SMilanka Ringwald                         break;
1076af2241c2SMilanka Ringwald                     }
1077af2241c2SMilanka Ringwald                     break;
1078af2241c2SMilanka Ringwald                 }
1079f4d3b82aSMilanka Ringwald                 default:
1080f4d3b82aSMilanka Ringwald                     break;
1081f4d3b82aSMilanka Ringwald             }
1082f4d3b82aSMilanka Ringwald 
1083f4d3b82aSMilanka Ringwald             break;
108483d7ed1cSMilanka Ringwald 
108570093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
108670093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
108770093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1088e7bd2dbeSMilanka Ringwald 
1089e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1090e7bd2dbeSMilanka Ringwald                 break;
1091e7bd2dbeSMilanka Ringwald             }
10927e1e6e7dSMilanka Ringwald 
1093e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1094e7bd2dbeSMilanka Ringwald             switch (client->state) {
109528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1096e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
109719146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
10983322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
109919146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1100556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
110119146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
110219146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1103556456ccSMilanka Ringwald #endif
11043322b222SMilanka Ringwald                     }
1105e7bd2dbeSMilanka Ringwald                     break;
1106e7bd2dbeSMilanka Ringwald 
110728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
110819146789SMilanka Ringwald 
110919146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1110021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1111021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
111219146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
111383d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1114021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1115021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
111683d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
111719146789SMilanka Ringwald     #endif
111819146789SMilanka Ringwald                     }
1119e7bd2dbeSMilanka Ringwald                     break;
1120e7bd2dbeSMilanka Ringwald 
1121e7bd2dbeSMilanka Ringwald                 default:
1122e7bd2dbeSMilanka Ringwald                     break;
112370093cf5SMilanka Ringwald             }
112470093cf5SMilanka Ringwald             break;
112570093cf5SMilanka Ringwald 
1126*fd39e93aSMilanka Ringwald         case GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE:
1127*fd39e93aSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_can_write_without_response_get_handle(packet));
1128*fd39e93aSMilanka Ringwald             btstack_assert(client != NULL);
1129*fd39e93aSMilanka Ringwald 
1130*fd39e93aSMilanka Ringwald             switch (client->state){
1131*fd39e93aSMilanka Ringwald                 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
1132*fd39e93aSMilanka Ringwald                     att_status = gatt_client_write_value_of_characteristic_without_response(
1133*fd39e93aSMilanka Ringwald                         client->con_handle,
1134*fd39e93aSMilanka Ringwald                         client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
1135*fd39e93aSMilanka Ringwald 
1136*fd39e93aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1137*fd39e93aSMilanka Ringwald                     printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
1138*fd39e93aSMilanka Ringwald #endif
1139*fd39e93aSMilanka Ringwald 
1140*fd39e93aSMilanka Ringwald                     if (att_status == ATT_ERROR_SUCCESS){
1141*fd39e93aSMilanka Ringwald                         client->services[client->service_index].protocol_mode = client->required_protocol_mode;
1142*fd39e93aSMilanka Ringwald                         if ((client->service_index + 1) < client->num_instances){
1143*fd39e93aSMilanka Ringwald                             client->service_index++;
1144*fd39e93aSMilanka Ringwald                             hids_run_for_client(client);
1145*fd39e93aSMilanka Ringwald                             break;
1146*fd39e93aSMilanka Ringwald                         }
1147*fd39e93aSMilanka Ringwald                     }
1148*fd39e93aSMilanka Ringwald 
1149*fd39e93aSMilanka Ringwald                     // read UUIDS for external characteristics
1150*fd39e93aSMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1151*fd39e93aSMilanka Ringwald                         hids_run_for_client(client);
1152*fd39e93aSMilanka Ringwald                         break;
1153*fd39e93aSMilanka Ringwald                     }
1154*fd39e93aSMilanka Ringwald 
1155*fd39e93aSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1156*fd39e93aSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
1157*fd39e93aSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1158*fd39e93aSMilanka Ringwald                         hids_run_for_client(client);
1159*fd39e93aSMilanka Ringwald                         break;
1160*fd39e93aSMilanka Ringwald                     }
1161*fd39e93aSMilanka Ringwald 
1162*fd39e93aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1163*fd39e93aSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1164*fd39e93aSMilanka Ringwald                     break;
1165*fd39e93aSMilanka Ringwald 
1166*fd39e93aSMilanka Ringwald                 case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
1167*fd39e93aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1168*fd39e93aSMilanka Ringwald                     printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
1169*fd39e93aSMilanka Ringwald #endif
1170*fd39e93aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1171*fd39e93aSMilanka Ringwald                     (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
1172*fd39e93aSMilanka Ringwald                     break;
1173*fd39e93aSMilanka Ringwald 
1174*fd39e93aSMilanka Ringwald                 default:
1175*fd39e93aSMilanka Ringwald                     break;
1176*fd39e93aSMilanka Ringwald             }
1177*fd39e93aSMilanka Ringwald 
1178*fd39e93aSMilanka Ringwald             break;
1179*fd39e93aSMilanka Ringwald 
1180cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1181cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1182cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1183cf26c8fbSMilanka Ringwald 
1184cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1185cf26c8fbSMilanka Ringwald 
1186cf26c8fbSMilanka Ringwald             switch (client->state){
1187cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
11886bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11896bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11906bcfb631SMilanka Ringwald                         hids_finalize_client(client);
1191cf26c8fbSMilanka Ringwald                         break;
11926bcfb631SMilanka Ringwald                     }
11936bcfb631SMilanka Ringwald 
11946bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
11956bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
11966bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11976bcfb631SMilanka Ringwald                         break;
11986bcfb631SMilanka Ringwald                     }
11996bcfb631SMilanka Ringwald 
12006bcfb631SMilanka Ringwald                     client->service_index = 0;
120119146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12026bcfb631SMilanka Ringwald                     break;
12036bcfb631SMilanka Ringwald 
12046bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
12056bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12066bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12076bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12086bcfb631SMilanka Ringwald                         break;
12096bcfb631SMilanka Ringwald                     }
12106bcfb631SMilanka Ringwald 
121170093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
121270093cf5SMilanka Ringwald                         // discover characteristics of next service
121370093cf5SMilanka Ringwald                         client->service_index++;
121470093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1215021192e1SMilanka Ringwald                         break;
1216021192e1SMilanka Ringwald                     }
1217021192e1SMilanka Ringwald 
1218*fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1219*fd39e93aSMilanka Ringwald 
1220021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1221021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1222*fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1223*fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1224021192e1SMilanka Ringwald                             }
12257e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12267e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12277e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
122870093cf5SMilanka Ringwald                                 break;
122970093cf5SMilanka Ringwald                             }
123070093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
123170093cf5SMilanka Ringwald                             hids_finalize_client(client);
1232*fd39e93aSMilanka Ringwald                             return;
1233*fd39e93aSMilanka Ringwald 
1234*fd39e93aSMilanka Ringwald                         default:
1235*fd39e93aSMilanka Ringwald                             // set boot mode
1236*fd39e93aSMilanka Ringwald                             client->service_index = 0;
1237*fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1238*fd39e93aSMilanka Ringwald                             break;
1239*fd39e93aSMilanka Ringwald                     }
12406bcfb631SMilanka Ringwald                     break;
12416bcfb631SMilanka Ringwald 
12423322b222SMilanka Ringwald 
124328da36a6SMilanka Ringwald                 // HID descriptor found
124428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1245e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1246e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1247e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1248e7bd2dbeSMilanka Ringwald                         break;
1249e7bd2dbeSMilanka Ringwald                     }
1250e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1251e7bd2dbeSMilanka Ringwald                     break;
1252e7bd2dbeSMilanka Ringwald 
125328da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1254e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12553322b222SMilanka Ringwald                     // go for next report map
12563322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1257e7bd2dbeSMilanka Ringwald                         break;
1258e7bd2dbeSMilanka Ringwald                     }
1259e7bd2dbeSMilanka Ringwald 
12603322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12613322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1262e7bd2dbeSMilanka Ringwald                         break;
1263e7bd2dbeSMilanka Ringwald                     }
1264e7bd2dbeSMilanka Ringwald 
1265e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1266e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12677e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1268e7bd2dbeSMilanka Ringwald                         break;
1269e7bd2dbeSMilanka Ringwald                     }
1270e7bd2dbeSMilanka Ringwald 
1271e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1272e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1273e7bd2dbeSMilanka Ringwald                     break;
1274e7bd2dbeSMilanka Ringwald 
127528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1276e7bd2dbeSMilanka Ringwald                     // go for next map report
12773322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1278e7bd2dbeSMilanka Ringwald                         break;
1279e7bd2dbeSMilanka Ringwald                     }
1280e7bd2dbeSMilanka Ringwald 
12813322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12823322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1283e7bd2dbeSMilanka Ringwald                     break;
1284e7bd2dbeSMilanka Ringwald 
12853322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1286e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1287e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12887e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1289e7bd2dbeSMilanka Ringwald                         break;
1290e7bd2dbeSMilanka Ringwald                     }
1291e7bd2dbeSMilanka Ringwald 
1292e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1293e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1294e7bd2dbeSMilanka Ringwald                     break;
1295e7bd2dbeSMilanka Ringwald 
129628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1297af2241c2SMilanka Ringwald                     if (client->handle != 0){
129828da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
129970093cf5SMilanka Ringwald                         break;
130070093cf5SMilanka Ringwald                     }
130170093cf5SMilanka Ringwald                     // go for next report
13027e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
130370093cf5SMilanka Ringwald                         break;
130470093cf5SMilanka Ringwald                     }
1305835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
130670093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
130770093cf5SMilanka Ringwald                     break;
130870093cf5SMilanka Ringwald 
130928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
131070093cf5SMilanka Ringwald                     // go for next report
13117e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
131270093cf5SMilanka Ringwald                         break;
131370093cf5SMilanka Ringwald                     }
1314021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1315021192e1SMilanka Ringwald                         break;
1316021192e1SMilanka Ringwald                     }
1317835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
131870093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
131970093cf5SMilanka Ringwald                     break;
132070093cf5SMilanka Ringwald 
1321021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1322021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13232901a9b7SMilanka Ringwald                         break;
13242901a9b7SMilanka Ringwald                     }
1325835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13266bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13276bcfb631SMilanka Ringwald                     break;
1328f4d3b82aSMilanka Ringwald 
1329cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1330cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1331cd28e7b1SMilanka Ringwald                         break;
1332cd28e7b1SMilanka Ringwald                     }
1333cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1334cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1335cd28e7b1SMilanka Ringwald                     break;
1336cd28e7b1SMilanka Ringwald 
1337835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1338835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1339835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1340835a13f1SMilanka Ringwald                     break;
1341835a13f1SMilanka Ringwald #endif
1342f4d3b82aSMilanka Ringwald 
1343af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
13446d6f7efcSMilanka Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1345f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1346f4d3b82aSMilanka Ringwald                     break;
1347f4d3b82aSMilanka Ringwald 
13486d6f7efcSMilanka Ringwald 
1349cf26c8fbSMilanka Ringwald                 default:
1350cf26c8fbSMilanka Ringwald                     break;
1351cf26c8fbSMilanka Ringwald             }
1352cf26c8fbSMilanka Ringwald             break;
1353cf26c8fbSMilanka Ringwald 
1354cf26c8fbSMilanka Ringwald         default:
1355cf26c8fbSMilanka Ringwald             break;
1356cf26c8fbSMilanka Ringwald     }
13576bcfb631SMilanka Ringwald 
13586bcfb631SMilanka Ringwald     if (client != NULL){
13596bcfb631SMilanka Ringwald         hids_run_for_client(client);
13606bcfb631SMilanka Ringwald     }
1361cf26c8fbSMilanka Ringwald }
1362cf26c8fbSMilanka Ringwald 
13636bcfb631SMilanka 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){
1364cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1365cf26c8fbSMilanka Ringwald 
1366cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1367cf26c8fbSMilanka Ringwald     if (client != NULL){
1368cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1369cf26c8fbSMilanka Ringwald     }
1370cf26c8fbSMilanka Ringwald 
1371cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1372cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1373cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1374cf26c8fbSMilanka Ringwald     }
1375cf26c8fbSMilanka Ringwald 
1376cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1377cf26c8fbSMilanka Ringwald     if (client == NULL) {
1378cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1379cf26c8fbSMilanka Ringwald     }
1380cf26c8fbSMilanka Ringwald 
13816bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1382cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1383cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1384cf26c8fbSMilanka Ringwald 
1385cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1386cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1387cf26c8fbSMilanka Ringwald }
1388cf26c8fbSMilanka Ringwald 
1389cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1390cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1391cf26c8fbSMilanka Ringwald     if (client == NULL){
1392cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1393cf26c8fbSMilanka Ringwald     }
1394cf26c8fbSMilanka Ringwald     // finalize connection
1395cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1396cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1397cf26c8fbSMilanka Ringwald }
1398cf26c8fbSMilanka Ringwald 
1399*fd39e93aSMilanka Ringwald uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type, const uint8_t * report, uint8_t report_len){
14001624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14011624214bSMilanka Ringwald     if (client == NULL){
14021624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14031624214bSMilanka Ringwald     }
14041624214bSMilanka Ringwald 
14051624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14061624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14071624214bSMilanka Ringwald     }
14081624214bSMilanka Ringwald 
1409*fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
141083d7ed1cSMilanka Ringwald 
14111624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14121624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14131624214bSMilanka Ringwald     }
14141624214bSMilanka Ringwald 
14151624214bSMilanka Ringwald     uint16_t mtu;
14161624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14171624214bSMilanka Ringwald 
14181624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14191624214bSMilanka Ringwald         return status;
14201624214bSMilanka Ringwald     }
14211624214bSMilanka Ringwald 
14221624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14231624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14241624214bSMilanka Ringwald     }
14251624214bSMilanka Ringwald 
14266d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1427021192e1SMilanka Ringwald     client->report_index = report_index;
14281624214bSMilanka Ringwald     client->report = report;
14291624214bSMilanka Ringwald     client->report_len = report_len;
14301624214bSMilanka Ringwald 
14311624214bSMilanka Ringwald     hids_run_for_client(client);
14321624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14331624214bSMilanka Ringwald }
14341624214bSMilanka Ringwald 
1435*fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
143683d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
143783d7ed1cSMilanka Ringwald     if (client == NULL){
143883d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
143983d7ed1cSMilanka Ringwald     }
144083d7ed1cSMilanka Ringwald 
144183d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
144283d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
144383d7ed1cSMilanka Ringwald     }
144483d7ed1cSMilanka Ringwald 
1445*fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
144683d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
144783d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
144883d7ed1cSMilanka Ringwald     }
144983d7ed1cSMilanka Ringwald 
145083d7ed1cSMilanka Ringwald     client->report_index = report_index;
145183d7ed1cSMilanka Ringwald 
145283d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
145383d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
145483d7ed1cSMilanka Ringwald #else
145583d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
145683d7ed1cSMilanka Ringwald #endif
145783d7ed1cSMilanka Ringwald     hids_run_for_client(client);
145883d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
145983d7ed1cSMilanka Ringwald }
146083d7ed1cSMilanka Ringwald 
146183d7ed1cSMilanka Ringwald 
1462f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1463f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1464f4d3b82aSMilanka Ringwald     if (client == NULL){
1465f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1466f4d3b82aSMilanka Ringwald     }
1467f4d3b82aSMilanka Ringwald 
1468f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1469f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1470f4d3b82aSMilanka Ringwald     }
1471f4d3b82aSMilanka Ringwald 
1472f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1473f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1474f4d3b82aSMilanka Ringwald     }
1475f4d3b82aSMilanka Ringwald 
1476f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1477af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1478af2241c2SMilanka Ringwald 
1479af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1480af2241c2SMilanka Ringwald     hids_run_for_client(client);
1481af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1482af2241c2SMilanka Ringwald }
1483af2241c2SMilanka Ringwald 
1484af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1485af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1486af2241c2SMilanka Ringwald     if (client == NULL){
1487af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1488af2241c2SMilanka Ringwald     }
1489af2241c2SMilanka Ringwald 
1490af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1491af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1492af2241c2SMilanka Ringwald     }
1493af2241c2SMilanka Ringwald 
1494af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1495af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1496af2241c2SMilanka Ringwald     }
1497af2241c2SMilanka Ringwald 
1498af2241c2SMilanka Ringwald     client->service_index = service_index;
1499af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1500af2241c2SMilanka Ringwald 
1501af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1502f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1503f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1504f4d3b82aSMilanka Ringwald }
1505f4d3b82aSMilanka Ringwald 
15066d6f7efcSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, hid_protocol_mode_t protocol_mode, uint8_t service_index){
15076d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15086d6f7efcSMilanka Ringwald     if (client == NULL){
15096d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15106d6f7efcSMilanka Ringwald     }
15116d6f7efcSMilanka Ringwald 
15126d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15136d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15146d6f7efcSMilanka Ringwald     }
15156d6f7efcSMilanka Ringwald 
15166d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15176d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15186d6f7efcSMilanka Ringwald     }
15196d6f7efcSMilanka Ringwald 
15206d6f7efcSMilanka Ringwald     client->service_index = service_index;
15216d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15226d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15236d6f7efcSMilanka Ringwald 
15246d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15256d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15266d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15276d6f7efcSMilanka Ringwald }
15286d6f7efcSMilanka Ringwald 
15296d6f7efcSMilanka Ringwald 
15306d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15316d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15326d6f7efcSMilanka Ringwald     if (client == NULL){
15336d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15346d6f7efcSMilanka Ringwald     }
15356d6f7efcSMilanka Ringwald 
15366d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15376d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15386d6f7efcSMilanka Ringwald     }
15396d6f7efcSMilanka Ringwald 
15406d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15416d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15426d6f7efcSMilanka Ringwald     }
15436d6f7efcSMilanka Ringwald 
15446d6f7efcSMilanka Ringwald     client->service_index = service_index;
15456d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15466d6f7efcSMilanka Ringwald     client->value = value;
15476d6f7efcSMilanka Ringwald 
15486d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15496d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15506d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15516d6f7efcSMilanka Ringwald }
15526d6f7efcSMilanka Ringwald 
15536d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15546d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15556d6f7efcSMilanka Ringwald }
15566d6f7efcSMilanka Ringwald 
15576d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
15586d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
15596d6f7efcSMilanka Ringwald }
15606d6f7efcSMilanka Ringwald 
1561cd28e7b1SMilanka Ringwald uint8_t hids_client_send_enable_notifications(uint16_t hids_cid){
1562cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1563cd28e7b1SMilanka Ringwald     if (client == NULL){
1564cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1565cd28e7b1SMilanka Ringwald     }
1566cd28e7b1SMilanka Ringwald 
1567cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1568cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1569cd28e7b1SMilanka Ringwald     }
1570cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1571cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1572cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1573cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1574cd28e7b1SMilanka Ringwald     }
1575cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1576cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1577cd28e7b1SMilanka Ringwald }
1578cd28e7b1SMilanka Ringwald 
1579cd28e7b1SMilanka Ringwald uint8_t hids_client_send_disable_notifications(uint16_t hids_cid){
1580cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1581cd28e7b1SMilanka Ringwald     if (client == NULL){
1582cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1583cd28e7b1SMilanka Ringwald     }
1584cd28e7b1SMilanka Ringwald 
1585cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1586cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1587cd28e7b1SMilanka Ringwald     }
1588cd28e7b1SMilanka Ringwald 
1589cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1590cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1591cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1592cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1593cd28e7b1SMilanka Ringwald     }
1594cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1595cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1596cd28e7b1SMilanka Ringwald }
15976d6f7efcSMilanka Ringwald 
1598021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1599021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1600021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1601021192e1SMilanka Ringwald }
1602cf26c8fbSMilanka Ringwald 
1603cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1604