xref: /btstack/src/ble/gatt-service/hids_client.c (revision 1fa7278ece93fee3ec13579f87682ba4e093c5bd)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/core.h"
53fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
54cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
55fc975d0eSMilanka Ringwald #include "btstack_debug.h"
56fc975d0eSMilanka Ringwald #include "btstack_event.h"
57fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
58fc975d0eSMilanka Ringwald #include "gap.h"
59fc975d0eSMilanka Ringwald 
6070093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6470093cf5SMilanka Ringwald 
65da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
66da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
67da142a6fSMilanka Ringwald 
68da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
69da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
70da142a6fSMilanka Ringwald 
71cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
72*1fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context);
73cf26c8fbSMilanka Ringwald 
74556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
75556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
76556456ccSMilanka Ringwald     switch (uuid){
77556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
78556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
79556456ccSMilanka Ringwald 
80556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
81556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
82556456ccSMilanka Ringwald 
83556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
84556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
85556456ccSMilanka Ringwald 
86556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
87556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
88556456ccSMilanka Ringwald 
89556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
90556456ccSMilanka Ringwald             return "REPORT";
91556456ccSMilanka Ringwald 
92556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
93556456ccSMilanka Ringwald             return "REPORT_MAP";
94556456ccSMilanka Ringwald 
95556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
96556456ccSMilanka Ringwald             return "HID_INFORMATION";
97556456ccSMilanka Ringwald 
98556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
99556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
100556456ccSMilanka Ringwald         default:
101556456ccSMilanka Ringwald             return "UKNOWN";
102556456ccSMilanka Ringwald     }
103556456ccSMilanka Ringwald }
104556456ccSMilanka Ringwald #endif
105556456ccSMilanka Ringwald 
106da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
107da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
109da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
110da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
111da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
112da142a6fSMilanka Ringwald         return client;
113da142a6fSMilanka Ringwald     }
114da142a6fSMilanka Ringwald     return NULL;
115da142a6fSMilanka Ringwald }
116da142a6fSMilanka Ringwald 
117da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
118da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
120da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
121da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
122da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
123da142a6fSMilanka Ringwald         return client;
124da142a6fSMilanka Ringwald     }
125da142a6fSMilanka Ringwald     return NULL;
126da142a6fSMilanka Ringwald }
127da142a6fSMilanka Ringwald 
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald // START Descriptor Storage Util
130da142a6fSMilanka Ringwald 
131da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
132da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
133da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
134da142a6fSMilanka Ringwald     uint8_t i;
135da142a6fSMilanka Ringwald 
136da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
137da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
138da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
139da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
140da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
141da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
142da142a6fSMilanka Ringwald         }
143da142a6fSMilanka Ringwald     }
144da142a6fSMilanka Ringwald     return free_space;
145da142a6fSMilanka Ringwald }
146da142a6fSMilanka Ringwald 
147da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
148da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
150da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
151da142a6fSMilanka Ringwald }
152da142a6fSMilanka Ringwald 
153da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
154da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
155da142a6fSMilanka Ringwald 
156da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
157da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
158da142a6fSMilanka Ringwald     return true;
159da142a6fSMilanka Ringwald }
160da142a6fSMilanka Ringwald 
161da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
162da142a6fSMilanka Ringwald     uint8_t service_index = 0;
163da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
164da142a6fSMilanka Ringwald 
165da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
166da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
167da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
168da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
169da142a6fSMilanka Ringwald     }
170da142a6fSMilanka Ringwald 
171da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
172da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
173da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
174da142a6fSMilanka Ringwald 
175da142a6fSMilanka Ringwald     uint8_t i;
176da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
177da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
178da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
179da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
180da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
181da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
182da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
183da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
184da142a6fSMilanka Ringwald             }
185da142a6fSMilanka Ringwald         }
186da142a6fSMilanka Ringwald     }
187da142a6fSMilanka Ringwald }
188da142a6fSMilanka Ringwald 
189da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
190da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
191021192e1SMilanka Ringwald     if (client == NULL){
192021192e1SMilanka Ringwald         return NULL;
193021192e1SMilanka Ringwald     }
194021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
195da142a6fSMilanka Ringwald         return NULL;
196da142a6fSMilanka Ringwald     }
197da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
198da142a6fSMilanka Ringwald }
199da142a6fSMilanka Ringwald 
200da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
201da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
202021192e1SMilanka Ringwald     if (client == NULL){
203021192e1SMilanka Ringwald         return 0;
204021192e1SMilanka Ringwald     }
205021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
206da142a6fSMilanka Ringwald         return 0;
207da142a6fSMilanka Ringwald     }
208da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
209da142a6fSMilanka Ringwald }
210da142a6fSMilanka Ringwald 
211da142a6fSMilanka Ringwald // END Descriptor Storage Util
212da142a6fSMilanka Ringwald 
213cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
214cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
215cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
216cf26c8fbSMilanka Ringwald     } else {
217cf26c8fbSMilanka Ringwald         hids_cid_counter++;
218cf26c8fbSMilanka Ringwald     }
219cf26c8fbSMilanka Ringwald     return hids_cid_counter;
220fc975d0eSMilanka Ringwald }
221fc975d0eSMilanka Ringwald 
222ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
223ab116b1cSMilanka Ringwald     uint8_t i;
224a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
225ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
226ab116b1cSMilanka Ringwald             return i;
227ab116b1cSMilanka Ringwald         }
228ab116b1cSMilanka Ringwald     }
229ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
230ab116b1cSMilanka Ringwald }
231ab116b1cSMilanka Ringwald 
23219146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
23319146789SMilanka Ringwald     uint8_t i;
23419146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
23519146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
23619146789SMilanka Ringwald             return i;
23719146789SMilanka Ringwald         }
23819146789SMilanka Ringwald     }
23919146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
24019146789SMilanka Ringwald }
24119146789SMilanka Ringwald 
24219146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
24319146789SMilanka Ringwald     uint8_t i;
24419146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24519146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
24619146789SMilanka Ringwald             return true;
24719146789SMilanka Ringwald         }
24819146789SMilanka Ringwald     }
24919146789SMilanka Ringwald     return false;
25019146789SMilanka Ringwald }
25119146789SMilanka Ringwald 
252fd39e93aSMilanka 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){
25383d7ed1cSMilanka Ringwald     uint8_t i;
254835a13f1SMilanka Ringwald 
25583d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
256fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
257fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
258fd39e93aSMilanka Ringwald 
259fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
26083d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
26183d7ed1cSMilanka Ringwald                 continue;
26283d7ed1cSMilanka Ringwald             }
263fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
26483d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
26583d7ed1cSMilanka Ringwald                 continue;
26683d7ed1cSMilanka Ringwald             }
267fd39e93aSMilanka Ringwald         }
268fd39e93aSMilanka Ringwald 
269fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
27083d7ed1cSMilanka Ringwald             return i;
27183d7ed1cSMilanka Ringwald         }
27283d7ed1cSMilanka Ringwald     }
27383d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
27483d7ed1cSMilanka Ringwald }
27519146789SMilanka Ringwald 
276021192e1SMilanka 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){
277ab116b1cSMilanka Ringwald 
27819146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
279ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2803322b222SMilanka Ringwald         return report_index;
281ab116b1cSMilanka Ringwald     }
28228da36a6SMilanka Ringwald     report_index = client->num_reports;
283ab116b1cSMilanka Ringwald 
28428da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
28528da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
28628da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
28728da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
288ab116b1cSMilanka Ringwald 
28928da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
29028da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
29128da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
292021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
29370093cf5SMilanka Ringwald 
294021192e1SMilanka 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);
295ab116b1cSMilanka Ringwald         client->num_reports++;
2963322b222SMilanka Ringwald         return report_index;
297ab116b1cSMilanka Ringwald     } else {
298ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
2993322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
300ab116b1cSMilanka Ringwald     }
301ab116b1cSMilanka Ringwald }
302ab116b1cSMilanka Ringwald 
30319146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
304556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
305556456ccSMilanka Ringwald 
306556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
30719146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
308556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
30919146789SMilanka Ringwald 
310556456ccSMilanka Ringwald         client->num_external_reports++;
31119146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
312556456ccSMilanka Ringwald         return report_index;
313556456ccSMilanka Ringwald     } else {
314556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
315556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
31670093cf5SMilanka Ringwald     }
317556456ccSMilanka Ringwald }
318556456ccSMilanka Ringwald 
3197e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3207e1e6e7dSMilanka Ringwald     uint8_t i;
3213322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3223322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
323556456ccSMilanka Ringwald             return i;
3243322b222SMilanka Ringwald         }
3253322b222SMilanka Ringwald     }
326556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
327556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3283322b222SMilanka Ringwald }
3293322b222SMilanka Ringwald 
3303322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3313322b222SMilanka Ringwald     client->service_index++;
3323322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
333556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3343322b222SMilanka Ringwald         return true;
3353322b222SMilanka Ringwald     }
3363322b222SMilanka Ringwald     return false;
3373322b222SMilanka Ringwald }
3383322b222SMilanka Ringwald 
3393322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3403322b222SMilanka Ringwald     client->service_index = 0;
3413322b222SMilanka Ringwald 
3423322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3433322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3443322b222SMilanka Ringwald         return true;
3453322b222SMilanka Ringwald     }
3463322b222SMilanka Ringwald     return false;
3473322b222SMilanka Ringwald }
3483322b222SMilanka Ringwald 
3493322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
350021192e1SMilanka Ringwald     client->report_index++;
35119146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3523322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
353e7bd2dbeSMilanka Ringwald         return true;
354e7bd2dbeSMilanka Ringwald     }
355e7bd2dbeSMilanka Ringwald     return false;
356e7bd2dbeSMilanka Ringwald }
357e7bd2dbeSMilanka Ringwald 
3583322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
359021192e1SMilanka Ringwald     client->report_index = 0;
36019146789SMilanka Ringwald     if (client->num_external_reports > 0){
3613322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3627e1e6e7dSMilanka Ringwald         return true;
3637e1e6e7dSMilanka Ringwald     }
3647e1e6e7dSMilanka Ringwald     return false;
3657e1e6e7dSMilanka Ringwald }
3667e1e6e7dSMilanka Ringwald 
3677e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3687e1e6e7dSMilanka Ringwald     uint8_t i;
3697e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
370fd39e93aSMilanka Ringwald 
371021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
37270093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
373fd39e93aSMilanka Ringwald         if (!report.boot_report){
37470093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3757e1e6e7dSMilanka Ringwald                 index = i;
376021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3777e1e6e7dSMilanka Ringwald                 break;
37870093cf5SMilanka Ringwald             }
379fd39e93aSMilanka Ringwald         }
380fd39e93aSMilanka Ringwald     }
381021192e1SMilanka Ringwald     client->report_index = index;
3827e1e6e7dSMilanka Ringwald     return index;
3837e1e6e7dSMilanka Ringwald }
3847e1e6e7dSMilanka Ringwald 
3857e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
386021192e1SMilanka Ringwald     client->report_index++;
3877e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
38828da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
389e7bd2dbeSMilanka Ringwald         return true;
39070093cf5SMilanka Ringwald     }
3917e1e6e7dSMilanka Ringwald     return false;
3927e1e6e7dSMilanka Ringwald }
3937e1e6e7dSMilanka Ringwald 
3947e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
395021192e1SMilanka Ringwald     client->report_index = 0;
3967e1e6e7dSMilanka Ringwald 
3977e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
39828da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
3997e1e6e7dSMilanka Ringwald         return true;
4007e1e6e7dSMilanka Ringwald     }
401e7bd2dbeSMilanka Ringwald     return false;
40270093cf5SMilanka Ringwald }
403ab116b1cSMilanka Ringwald 
404021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
405021192e1SMilanka Ringwald     uint8_t i;
406021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
407021192e1SMilanka Ringwald 
408021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
409021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
410fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
411fd39e93aSMilanka Ringwald 
412fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
413fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
414021192e1SMilanka Ringwald                 continue;
415021192e1SMilanka Ringwald             }
416fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
417fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
418fd39e93aSMilanka Ringwald                 continue;
419fd39e93aSMilanka Ringwald             }
420fd39e93aSMilanka Ringwald         }
421fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
422021192e1SMilanka Ringwald             index = i;
423021192e1SMilanka Ringwald         }
424021192e1SMilanka Ringwald     }
425021192e1SMilanka Ringwald     client->report_index = index;
426021192e1SMilanka Ringwald     return index;
427021192e1SMilanka Ringwald }
428021192e1SMilanka Ringwald 
429021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
430021192e1SMilanka Ringwald     client->report_index++;
431021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
432021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
433021192e1SMilanka Ringwald         return true;
434021192e1SMilanka Ringwald     }
435021192e1SMilanka Ringwald     return false;
436021192e1SMilanka Ringwald }
437021192e1SMilanka Ringwald 
438021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
439cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
440cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
441cd28e7b1SMilanka Ringwald #endif
442021192e1SMilanka Ringwald     client->report_index = 0;
443021192e1SMilanka Ringwald 
444021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
445021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
446021192e1SMilanka Ringwald         return true;
447021192e1SMilanka Ringwald     }
448021192e1SMilanka Ringwald     return false;
449021192e1SMilanka Ringwald }
450021192e1SMilanka Ringwald 
451cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
452cd28e7b1SMilanka Ringwald     client->report_index++;
453cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
454cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
455cd28e7b1SMilanka Ringwald         return true;
456cd28e7b1SMilanka Ringwald     }
457cd28e7b1SMilanka Ringwald     return false;
458cd28e7b1SMilanka Ringwald }
459cd28e7b1SMilanka Ringwald 
460cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
461cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
462cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
463cd28e7b1SMilanka Ringwald #endif
464cd28e7b1SMilanka Ringwald     client->report_index = 0;
465cd28e7b1SMilanka Ringwald 
466cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
467cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
468cd28e7b1SMilanka Ringwald         return true;
469cd28e7b1SMilanka Ringwald     }
470cd28e7b1SMilanka Ringwald     return false;
471cd28e7b1SMilanka Ringwald }
472cd28e7b1SMilanka Ringwald 
473cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
474cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
475cf26c8fbSMilanka Ringwald     if (!client){
476cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
477cf26c8fbSMilanka Ringwald         return NULL;
478cf26c8fbSMilanka Ringwald     }
479cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
480cf26c8fbSMilanka Ringwald     client->cid = cid;
481cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
482fc975d0eSMilanka Ringwald 
483cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
484cf26c8fbSMilanka Ringwald     return client;
485fc975d0eSMilanka Ringwald }
486fc975d0eSMilanka Ringwald 
487cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
488021192e1SMilanka Ringwald     // stop listening
489021192e1SMilanka Ringwald     uint8_t i;
490021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
491021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
492021192e1SMilanka Ringwald     }
493021192e1SMilanka Ringwald 
494da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
495cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
496cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
497fc975d0eSMilanka Ringwald }
498cf26c8fbSMilanka Ringwald 
499cf26c8fbSMilanka Ringwald 
500cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5016bcfb631SMilanka Ringwald     uint8_t event[8];
502cf26c8fbSMilanka Ringwald     int pos = 0;
503cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
504cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
505cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
506cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
507cf26c8fbSMilanka Ringwald     pos += 2;
508cf26c8fbSMilanka Ringwald     event[pos++] = status;
509fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
510cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
511cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
512cf26c8fbSMilanka Ringwald }
513cf26c8fbSMilanka Ringwald 
514cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
515cd28e7b1SMilanka Ringwald     uint8_t event[6];
516cd28e7b1SMilanka Ringwald     int pos = 0;
517cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
518cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
519cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
520cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
521cd28e7b1SMilanka Ringwald     pos += 2;
522cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
523cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
524cd28e7b1SMilanka Ringwald }
525cd28e7b1SMilanka Ringwald 
526021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
527021192e1SMilanka Ringwald     uint16_t pos = 0;
528021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
529021192e1SMilanka Ringwald     pos++;  // skip len
530021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
531021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
532021192e1SMilanka Ringwald     pos += 2;
533021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
534021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
535021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
536021192e1SMilanka Ringwald     pos += 2;
537021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
53883d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
53983d7ed1cSMilanka Ringwald 
540021192e1SMilanka Ringwald }
541021192e1SMilanka Ringwald 
542f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
543f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
544f4d3b82aSMilanka Ringwald 
545f4d3b82aSMilanka Ringwald     uint8_t event[11];
546f4d3b82aSMilanka Ringwald     int pos = 0;
547f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
548f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
549f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
550f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
551f4d3b82aSMilanka Ringwald     pos += 2;
552f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
553f4d3b82aSMilanka Ringwald 
554f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
555f4d3b82aSMilanka Ringwald     pos += 3;
556f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
557f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
558f4d3b82aSMilanka Ringwald 
559f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
560f4d3b82aSMilanka Ringwald }
561f4d3b82aSMilanka Ringwald 
562af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
563af2241c2SMilanka Ringwald     if (value_len != 1) return;
564af2241c2SMilanka Ringwald 
565af2241c2SMilanka Ringwald     uint8_t event[11];
566af2241c2SMilanka Ringwald     int pos = 0;
567af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
568af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
569af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
570af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
571af2241c2SMilanka Ringwald     pos += 2;
572af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
573af2241c2SMilanka Ringwald     event[pos++] = value[0];
574af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
575af2241c2SMilanka Ringwald }
576f4d3b82aSMilanka Ringwald 
57783d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
578021192e1SMilanka Ringwald     UNUSED(packet_type);
579021192e1SMilanka Ringwald     UNUSED(channel);
58083d7ed1cSMilanka Ringwald 
581021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
582021192e1SMilanka Ringwald 
583021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
58417229983SMatthias Ringwald     if (client == NULL) return;
585021192e1SMilanka Ringwald 
586021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
587021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
588021192e1SMilanka Ringwald         return;
589021192e1SMilanka Ringwald     }
590021192e1SMilanka Ringwald 
59183d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
592021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
59383d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
594021192e1SMilanka Ringwald }
595021192e1SMilanka Ringwald 
59683d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
59783d7ed1cSMilanka Ringwald     UNUSED(packet_type);
59883d7ed1cSMilanka Ringwald     UNUSED(channel);
59983d7ed1cSMilanka Ringwald 
60083d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
60183d7ed1cSMilanka Ringwald 
60283d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
60317229983SMatthias Ringwald     if (client == NULL) return;
60483d7ed1cSMilanka Ringwald 
60583d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
60683d7ed1cSMilanka Ringwald         return;
60783d7ed1cSMilanka Ringwald     }
60883d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
60983d7ed1cSMilanka Ringwald 
61083d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
61183d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
61283d7ed1cSMilanka Ringwald         return;
61383d7ed1cSMilanka Ringwald     }
61483d7ed1cSMilanka Ringwald 
61583d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
61683d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
61783d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
61883d7ed1cSMilanka Ringwald }
619cf26c8fbSMilanka Ringwald 
620cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
621cf26c8fbSMilanka Ringwald     uint8_t att_status;
6226bcfb631SMilanka Ringwald     gatt_client_service_t service;
6236bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
624cf26c8fbSMilanka Ringwald 
625cf26c8fbSMilanka Ringwald     switch (client->state){
626cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
627556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
628556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
629556456ccSMilanka Ringwald #endif
630cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
63119146789SMilanka Ringwald 
63219146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
633cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
634cf26c8fbSMilanka Ringwald             UNUSED(att_status);
635cf26c8fbSMilanka Ringwald             break;
636cf26c8fbSMilanka Ringwald 
637cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
638556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
639556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
640556456ccSMilanka Ringwald #endif
6416bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6426bcfb631SMilanka Ringwald 
6436bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6446bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
64519146789SMilanka Ringwald 
64619146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6476bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6486bcfb631SMilanka Ringwald 
6496bcfb631SMilanka Ringwald             UNUSED(att_status);
6506bcfb631SMilanka Ringwald             break;
6516bcfb631SMilanka Ringwald 
65228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
653556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
654556456ccSMilanka 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);
655556456ccSMilanka Ringwald #endif
65628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
657556456ccSMilanka Ringwald 
65819146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
65919146789SMilanka 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);
660e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
661e7bd2dbeSMilanka Ringwald             break;
662e7bd2dbeSMilanka Ringwald 
663e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
664556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
665556456ccSMilanka 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);
666556456ccSMilanka Ringwald #endif
667e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
668e7bd2dbeSMilanka Ringwald 
6693322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6703322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6713322b222SMilanka Ringwald 
67219146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
673e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
674e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
675e7bd2dbeSMilanka Ringwald             break;
676e7bd2dbeSMilanka Ringwald 
67728da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
678556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
67919146789SMilanka 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);
680556456ccSMilanka Ringwald #endif
68128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
682e7bd2dbeSMilanka Ringwald 
68319146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
68419146789SMilanka 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);
685e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
686e7bd2dbeSMilanka Ringwald             break;
687e7bd2dbeSMilanka Ringwald 
6883322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
689556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
69019146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
691556456ccSMilanka Ringwald #endif
6923322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
693e7bd2dbeSMilanka Ringwald 
6943322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
6953322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
69619146789SMilanka Ringwald 
69719146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6983322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
699e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
700e7bd2dbeSMilanka Ringwald             break;
701e7bd2dbeSMilanka Ringwald 
70228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
703556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
70483d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
705556456ccSMilanka Ringwald                 client->report_index,
70683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
70783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
708556456ccSMilanka Ringwald #endif
70928da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
710af2241c2SMilanka Ringwald             client->handle = 0;
711556456ccSMilanka Ringwald 
712556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
713556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
714556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
715556456ccSMilanka Ringwald 
71619146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
71770093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
71870093cf5SMilanka Ringwald             UNUSED(att_status);
71970093cf5SMilanka Ringwald             break;
72070093cf5SMilanka Ringwald 
72128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
72228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
72370093cf5SMilanka Ringwald 
72419146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
725af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
726af2241c2SMilanka Ringwald             client->handle = 0;
72770093cf5SMilanka Ringwald             UNUSED(att_status);
72870093cf5SMilanka Ringwald             break;
72970093cf5SMilanka Ringwald 
730cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
731cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
732cd28e7b1SMilanka Ringwald             if (client->value > 0){
733cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
734cd28e7b1SMilanka Ringwald             } else {
735cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
736cd28e7b1SMilanka Ringwald             }
737cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
738cd28e7b1SMilanka Ringwald                 client->report_index,
739cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
740cd28e7b1SMilanka Ringwald #endif
741cd28e7b1SMilanka Ringwald 
742cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
743cd28e7b1SMilanka Ringwald 
744cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
745cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
746cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
747cd28e7b1SMilanka Ringwald 
748cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
749cd28e7b1SMilanka Ringwald 
750cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
751cd28e7b1SMilanka Ringwald 
752cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
753cd28e7b1SMilanka Ringwald                 switch(client->value){
754cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
755cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
756cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
757cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
758cd28e7b1SMilanka Ringwald                         break;
759cd28e7b1SMilanka Ringwald                     default:
760cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
761cd28e7b1SMilanka Ringwald                         break;
762cd28e7b1SMilanka Ringwald                 }
763cd28e7b1SMilanka Ringwald             } else {
764cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
765cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
766cd28e7b1SMilanka Ringwald                     break;
767cd28e7b1SMilanka Ringwald                 }
768cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
769cd28e7b1SMilanka Ringwald             }
770cd28e7b1SMilanka Ringwald             break;
771cd28e7b1SMilanka Ringwald 
772021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
773cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
774cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
775cd28e7b1SMilanka Ringwald                 client->report_index,
776cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
777cd28e7b1SMilanka Ringwald #endif
778021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
779021192e1SMilanka Ringwald 
780021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
781021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
782021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
783021192e1SMilanka Ringwald 
78419146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
785021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
786021192e1SMilanka Ringwald 
787cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
788cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
789cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
790cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
791cd28e7b1SMilanka Ringwald             } else {
792021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
793021192e1SMilanka Ringwald                     hids_run_for_client(client);
794021192e1SMilanka Ringwald                     break;
795021192e1SMilanka Ringwald                 }
796021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
797021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
798021192e1SMilanka Ringwald             }
799021192e1SMilanka Ringwald             break;
800021192e1SMilanka Ringwald 
80183d7ed1cSMilanka Ringwald 
8026d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
80383d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8046d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
80583d7ed1cSMilanka Ringwald                 client->report_index,
80683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
80783d7ed1cSMilanka Ringwald #endif
80883d7ed1cSMilanka Ringwald 
8096d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
81083d7ed1cSMilanka Ringwald 
8116d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8126d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
8136d6f7efcSMilanka Ringwald                 &handle_report_event, client->con_handle,
81483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
81583d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
81683d7ed1cSMilanka Ringwald             UNUSED(att_status);
81783d7ed1cSMilanka Ringwald             break;
81883d7ed1cSMilanka Ringwald 
81983d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
82083d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
821f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
822f4d3b82aSMilanka Ringwald                 client->report_index,
82383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
82483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82583d7ed1cSMilanka Ringwald #endif
82683d7ed1cSMilanka Ringwald 
82783d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
828f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
82983d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
83083d7ed1cSMilanka Ringwald                 &handle_report_event,
83183d7ed1cSMilanka Ringwald                 client->con_handle,
83283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
83383d7ed1cSMilanka Ringwald             UNUSED(att_status);
83483d7ed1cSMilanka Ringwald             break;
83583d7ed1cSMilanka Ringwald 
83683d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
83783d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
83883d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
83983d7ed1cSMilanka Ringwald 
840f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
84183d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
84283d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
84383d7ed1cSMilanka Ringwald                 client->con_handle,
84483d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
84583d7ed1cSMilanka Ringwald 
84683d7ed1cSMilanka Ringwald             break;
84783d7ed1cSMilanka Ringwald #endif
848af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
849af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
85083d7ed1cSMilanka Ringwald 
851f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
852f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
853f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
854f4d3b82aSMilanka Ringwald                 client->con_handle,
855af2241c2SMilanka Ringwald                 client->handle);
856f4d3b82aSMilanka Ringwald             break;
8576d6f7efcSMilanka Ringwald 
858fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8596d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
860*1fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
861*1fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
862*1fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
863*1fa7278eSMatthias Ringwald             break;
864*1fa7278eSMatthias Ringwald 
865*1fa7278eSMatthias Ringwald         default:
866*1fa7278eSMatthias Ringwald             break;
867*1fa7278eSMatthias Ringwald     }
868*1fa7278eSMatthias Ringwald }
869*1fa7278eSMatthias Ringwald 
870*1fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
871*1fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
872*1fa7278eSMatthias Ringwald     uint8_t att_status;
873*1fa7278eSMatthias Ringwald     switch (client->state){
874*1fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
875*1fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
876*1fa7278eSMatthias Ringwald                 client->con_handle,
877*1fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
878*1fa7278eSMatthias Ringwald 
879*1fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
880*1fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
881*1fa7278eSMatthias Ringwald #endif
882*1fa7278eSMatthias Ringwald 
883*1fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
884*1fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
885*1fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
886*1fa7278eSMatthias Ringwald                     client->service_index++;
887*1fa7278eSMatthias Ringwald                     hids_run_for_client(client);
888*1fa7278eSMatthias Ringwald                     break;
889*1fa7278eSMatthias Ringwald                 }
890*1fa7278eSMatthias Ringwald             }
891*1fa7278eSMatthias Ringwald 
892*1fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
893*1fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
894*1fa7278eSMatthias Ringwald                 hids_run_for_client(client);
895*1fa7278eSMatthias Ringwald                 break;
896*1fa7278eSMatthias Ringwald             }
897*1fa7278eSMatthias Ringwald 
898*1fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
899*1fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
900*1fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
901*1fa7278eSMatthias Ringwald                 hids_run_for_client(client);
902*1fa7278eSMatthias Ringwald                 break;
903*1fa7278eSMatthias Ringwald             }
904*1fa7278eSMatthias Ringwald 
905*1fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
906*1fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
907*1fa7278eSMatthias Ringwald             break;
908*1fa7278eSMatthias Ringwald 
909*1fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
910*1fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
911*1fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
912*1fa7278eSMatthias Ringwald #endif
913*1fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
914*1fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9156d6f7efcSMilanka Ringwald             break;
9166d6f7efcSMilanka Ringwald 
9176bcfb631SMilanka Ringwald         default:
9186bcfb631SMilanka Ringwald             break;
9196bcfb631SMilanka Ringwald     }
9206bcfb631SMilanka Ringwald }
9216bcfb631SMilanka Ringwald 
922cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
923cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
924cf26c8fbSMilanka Ringwald     UNUSED(channel);
925cf26c8fbSMilanka Ringwald     UNUSED(size);
926cf26c8fbSMilanka Ringwald 
9276bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
928cf26c8fbSMilanka Ringwald     uint8_t att_status;
9296bcfb631SMilanka Ringwald     gatt_client_service_t service;
9306bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
93170093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
932ab116b1cSMilanka Ringwald 
933021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
934021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
935e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9363322b222SMilanka Ringwald     uint8_t i;
9373322b222SMilanka Ringwald     uint8_t report_index;
938da142a6fSMilanka Ringwald 
939f4d3b82aSMilanka Ringwald     const uint8_t * value;
940f4d3b82aSMilanka Ringwald     uint16_t value_len;
941cf26c8fbSMilanka Ringwald 
942cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
943cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
944cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
94517229983SMatthias Ringwald             if (client == NULL) break;
946cf26c8fbSMilanka Ringwald 
947021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
948021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9496bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
950021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
951021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9526bcfb631SMilanka Ringwald                 client->num_instances++;
953021192e1SMilanka Ringwald 
954708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
955708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
956708c69d2SMilanka Ringwald #endif
957021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
958021192e1SMilanka Ringwald             }  else {
959021192e1SMilanka 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);
960da142a6fSMilanka Ringwald             }
9616bcfb631SMilanka Ringwald             break;
9626bcfb631SMilanka Ringwald 
9636bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9646bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
96517229983SMatthias Ringwald             if (client == NULL) break;
96617229983SMatthias Ringwald 
9676bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
96819146789SMilanka Ringwald 
96919146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
97019146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
97119146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
97219146789SMilanka Ringwald                     break;
97319146789SMilanka Ringwald                 }
97419146789SMilanka Ringwald             }
97519146789SMilanka Ringwald 
97619146789SMilanka Ringwald             switch (characteristic.uuid16){
97719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
978af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
97919146789SMilanka Ringwald                     break;
98019146789SMilanka Ringwald 
98119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
98219146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
98319146789SMilanka Ringwald                     break;
98419146789SMilanka Ringwald 
98519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
98619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
98719146789SMilanka Ringwald                     break;
98819146789SMilanka Ringwald 
98919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
990f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
99119146789SMilanka Ringwald                     break;
99219146789SMilanka Ringwald 
99319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
99419146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
99519146789SMilanka Ringwald                     break;
99619146789SMilanka Ringwald 
99719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
99819146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
99919146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
100019146789SMilanka Ringwald                     break;
100119146789SMilanka Ringwald 
100219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1003f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
100419146789SMilanka Ringwald                     break;
100519146789SMilanka Ringwald 
100619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1007f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
100819146789SMilanka Ringwald                     break;
100919146789SMilanka Ringwald 
101019146789SMilanka Ringwald                 default:
101119146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
101219146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
101319146789SMilanka Ringwald #endif
101419146789SMilanka Ringwald                     return;
101519146789SMilanka Ringwald             }
101619146789SMilanka Ringwald 
101719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
101819146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
101919146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
102019146789SMilanka Ringwald                 characteristic.start_handle,
102119146789SMilanka Ringwald                 characteristic.properties,
102219146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
102319146789SMilanka Ringwald                 client->service_index);
102419146789SMilanka Ringwald 
102519146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
102619146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
102719146789SMilanka Ringwald             }
102819146789SMilanka Ringwald             printf("\n");
102919146789SMilanka Ringwald #endif
1030cf26c8fbSMilanka Ringwald             break;
1031cf26c8fbSMilanka Ringwald 
10328cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1033e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10348cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
103517229983SMatthias Ringwald             if (client == NULL) break;
1036da142a6fSMilanka Ringwald 
1037f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1038f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10398cec2b74SMilanka Ringwald 
1040556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10418cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1042f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1043556456ccSMilanka Ringwald #endif
1044f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1045f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1046da142a6fSMilanka Ringwald                 if (!stored){
1047da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1048da142a6fSMilanka Ringwald                     break;
1049da142a6fSMilanka Ringwald                 }
1050da142a6fSMilanka Ringwald             }
1051e7bd2dbeSMilanka Ringwald             break;
1052e7bd2dbeSMilanka Ringwald 
105370093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
105470093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
105517229983SMatthias Ringwald             if (client == NULL) break;
105617229983SMatthias Ringwald 
105770093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
105870093cf5SMilanka Ringwald 
1059e7bd2dbeSMilanka Ringwald             switch (client->state) {
1060e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1061e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1062e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
106319146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10643322b222SMilanka Ringwald 
1065556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1066556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1067556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1068556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1069556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1070556456ccSMilanka Ringwald                                 client->service_index, report_index);
1071556456ccSMilanka Ringwald                         }
1072556456ccSMilanka Ringwald #endif
1073e7bd2dbeSMilanka Ringwald                     }
1074e7bd2dbeSMilanka Ringwald                     break;
107528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
107670093cf5SMilanka Ringwald                     // setup for descriptor value query
107770093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1078af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1079556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1080556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1081556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1082556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
108319146789SMilanka Ringwald #endif
1084556456ccSMilanka Ringwald                     }
1085556456ccSMilanka Ringwald 
108619146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1087556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
108883d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1089556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1090556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1091556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1092556456ccSMilanka Ringwald                     }
1093556456ccSMilanka Ringwald #endif
109470093cf5SMilanka Ringwald                     break;
1095556456ccSMilanka Ringwald 
1096e7bd2dbeSMilanka Ringwald                 default:
1097e7bd2dbeSMilanka Ringwald                     break;
1098e7bd2dbeSMilanka Ringwald             }
1099e7bd2dbeSMilanka Ringwald             break;
110070093cf5SMilanka Ringwald 
110183d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
110283d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
110317229983SMatthias Ringwald             if (client == NULL) break;
110483d7ed1cSMilanka Ringwald 
1105f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1106f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1107f4d3b82aSMilanka Ringwald 
1108af2241c2SMilanka Ringwald 
1109f4d3b82aSMilanka Ringwald             switch (client->state){
1110f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1111f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
111283d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1113f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
111483d7ed1cSMilanka Ringwald                     break;
111583d7ed1cSMilanka Ringwald #endif
1116af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1117af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1118af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1119f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1120f4d3b82aSMilanka Ringwald                         break;
1121af2241c2SMilanka Ringwald                     }
1122af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1123af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1124af2241c2SMilanka Ringwald                         break;
1125af2241c2SMilanka Ringwald                     }
1126af2241c2SMilanka Ringwald                     break;
1127af2241c2SMilanka Ringwald                 }
1128f4d3b82aSMilanka Ringwald                 default:
1129f4d3b82aSMilanka Ringwald                     break;
1130f4d3b82aSMilanka Ringwald             }
1131f4d3b82aSMilanka Ringwald 
1132f4d3b82aSMilanka Ringwald             break;
113383d7ed1cSMilanka Ringwald 
113470093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
113570093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
113617229983SMatthias Ringwald             if (client == NULL) break;
1137e7bd2dbeSMilanka Ringwald 
1138e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1139e7bd2dbeSMilanka Ringwald                 break;
1140e7bd2dbeSMilanka Ringwald             }
11417e1e6e7dSMilanka Ringwald 
1142e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1143e7bd2dbeSMilanka Ringwald             switch (client->state) {
114428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1145e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
114619146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11473322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
114819146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1149556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
115019146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
115119146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1152556456ccSMilanka Ringwald #endif
11533322b222SMilanka Ringwald                     }
1154e7bd2dbeSMilanka Ringwald                     break;
1155e7bd2dbeSMilanka Ringwald 
115628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
115719146789SMilanka Ringwald 
115819146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1159021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1160021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
116119146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
116283d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1163021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1164021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
116583d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
116619146789SMilanka Ringwald     #endif
116719146789SMilanka Ringwald                     }
1168e7bd2dbeSMilanka Ringwald                     break;
1169e7bd2dbeSMilanka Ringwald 
1170e7bd2dbeSMilanka Ringwald                 default:
1171e7bd2dbeSMilanka Ringwald                     break;
117270093cf5SMilanka Ringwald             }
117370093cf5SMilanka Ringwald             break;
117470093cf5SMilanka Ringwald 
1175cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1176cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
117717229983SMatthias Ringwald             if (client == NULL) break;
1178cf26c8fbSMilanka Ringwald 
1179cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1180cf26c8fbSMilanka Ringwald 
1181cf26c8fbSMilanka Ringwald             switch (client->state){
1182cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
11836bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11846bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11856bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11865fa2c39aSMilanka Ringwald                         return;
11876bcfb631SMilanka Ringwald                     }
11886bcfb631SMilanka Ringwald 
11896bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
11906bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
11916bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11925fa2c39aSMilanka Ringwald                         return;
11936bcfb631SMilanka Ringwald                     }
11946bcfb631SMilanka Ringwald 
11956bcfb631SMilanka Ringwald                     client->service_index = 0;
119619146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
11976bcfb631SMilanka Ringwald                     break;
11986bcfb631SMilanka Ringwald 
11996bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
12006bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12016bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12026bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12035fa2c39aSMilanka Ringwald                         return;
12046bcfb631SMilanka Ringwald                     }
12056bcfb631SMilanka Ringwald 
120670093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
120770093cf5SMilanka Ringwald                         // discover characteristics of next service
120870093cf5SMilanka Ringwald                         client->service_index++;
120970093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1210021192e1SMilanka Ringwald                         break;
1211021192e1SMilanka Ringwald                     }
1212021192e1SMilanka Ringwald 
1213fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1214fd39e93aSMilanka Ringwald 
1215021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1216021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1217fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1218fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1219021192e1SMilanka Ringwald                             }
12207e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12217e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12227e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
122370093cf5SMilanka Ringwald                                 break;
122470093cf5SMilanka Ringwald                             }
122570093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
122670093cf5SMilanka Ringwald                             hids_finalize_client(client);
1227fd39e93aSMilanka Ringwald                             return;
1228fd39e93aSMilanka Ringwald 
1229fd39e93aSMilanka Ringwald                         default:
1230fd39e93aSMilanka Ringwald                             // set boot mode
1231fd39e93aSMilanka Ringwald                             client->service_index = 0;
1232fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1233fd39e93aSMilanka Ringwald                             break;
1234fd39e93aSMilanka Ringwald                     }
12356bcfb631SMilanka Ringwald                     break;
12366bcfb631SMilanka Ringwald 
12373322b222SMilanka Ringwald 
123828da36a6SMilanka Ringwald                 // HID descriptor found
123928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1240e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1241e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1242e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12435fa2c39aSMilanka Ringwald                         return;
1244e7bd2dbeSMilanka Ringwald                     }
1245e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1246e7bd2dbeSMilanka Ringwald                     break;
1247e7bd2dbeSMilanka Ringwald 
124828da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1249e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12503322b222SMilanka Ringwald                     // go for next report map
12513322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1252e7bd2dbeSMilanka Ringwald                         break;
1253e7bd2dbeSMilanka Ringwald                     }
1254e7bd2dbeSMilanka Ringwald 
12553322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12563322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1257e7bd2dbeSMilanka Ringwald                         break;
1258e7bd2dbeSMilanka Ringwald                     }
1259e7bd2dbeSMilanka Ringwald 
1260e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1261e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12627e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1263e7bd2dbeSMilanka Ringwald                         break;
1264e7bd2dbeSMilanka Ringwald                     }
1265e7bd2dbeSMilanka Ringwald 
1266e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1267e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
12685fa2c39aSMilanka Ringwald                     return;
1269e7bd2dbeSMilanka Ringwald 
127028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1271e7bd2dbeSMilanka Ringwald                     // go for next map report
12723322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1273e7bd2dbeSMilanka Ringwald                         break;
1274e7bd2dbeSMilanka Ringwald                     }
1275e7bd2dbeSMilanka Ringwald 
12763322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12773322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1278e7bd2dbeSMilanka Ringwald                     break;
1279e7bd2dbeSMilanka Ringwald 
12803322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1281e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1282e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12837e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1284e7bd2dbeSMilanka Ringwald                         break;
1285e7bd2dbeSMilanka Ringwald                     }
1286e7bd2dbeSMilanka Ringwald 
1287e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1288e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
12895fa2c39aSMilanka Ringwald                     return;
1290e7bd2dbeSMilanka Ringwald 
129128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1292af2241c2SMilanka Ringwald                     if (client->handle != 0){
129328da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
129470093cf5SMilanka Ringwald                         break;
129570093cf5SMilanka Ringwald                     }
129670093cf5SMilanka Ringwald                     // go for next report
12977e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
129870093cf5SMilanka Ringwald                         break;
129970093cf5SMilanka Ringwald                     }
1300835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
130170093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
130270093cf5SMilanka Ringwald                     break;
130370093cf5SMilanka Ringwald 
130428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
130570093cf5SMilanka Ringwald                     // go for next report
13067e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
130770093cf5SMilanka Ringwald                         break;
130870093cf5SMilanka Ringwald                     }
1309021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1310021192e1SMilanka Ringwald                         break;
1311021192e1SMilanka Ringwald                     }
1312835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
131370093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
131470093cf5SMilanka Ringwald                     break;
131570093cf5SMilanka Ringwald 
1316021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1317021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13182901a9b7SMilanka Ringwald                         break;
13192901a9b7SMilanka Ringwald                     }
1320835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13216bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13226bcfb631SMilanka Ringwald                     break;
1323f4d3b82aSMilanka Ringwald 
1324cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1325cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1326cd28e7b1SMilanka Ringwald                         break;
1327cd28e7b1SMilanka Ringwald                     }
1328cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1329cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1330cd28e7b1SMilanka Ringwald                     break;
1331cd28e7b1SMilanka Ringwald 
1332835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1333835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1334835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1335835a13f1SMilanka Ringwald                     break;
1336835a13f1SMilanka Ringwald #endif
1337f4d3b82aSMilanka Ringwald 
1338af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
13396d6f7efcSMilanka Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1340f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1341f4d3b82aSMilanka Ringwald                     break;
1342f4d3b82aSMilanka Ringwald 
13436d6f7efcSMilanka Ringwald 
1344cf26c8fbSMilanka Ringwald                 default:
1345cf26c8fbSMilanka Ringwald                     break;
1346cf26c8fbSMilanka Ringwald             }
1347cf26c8fbSMilanka Ringwald             break;
1348cf26c8fbSMilanka Ringwald 
1349cf26c8fbSMilanka Ringwald         default:
1350cf26c8fbSMilanka Ringwald             break;
1351cf26c8fbSMilanka Ringwald     }
13526bcfb631SMilanka Ringwald 
13536bcfb631SMilanka Ringwald     if (client != NULL){
13546bcfb631SMilanka Ringwald         hids_run_for_client(client);
13556bcfb631SMilanka Ringwald     }
1356cf26c8fbSMilanka Ringwald }
1357cf26c8fbSMilanka Ringwald 
13586bcfb631SMilanka 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){
1359cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1360cf26c8fbSMilanka Ringwald 
1361cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1362cf26c8fbSMilanka Ringwald     if (client != NULL){
1363cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1364cf26c8fbSMilanka Ringwald     }
1365cf26c8fbSMilanka Ringwald 
1366cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1367cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1368cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1369cf26c8fbSMilanka Ringwald     }
1370cf26c8fbSMilanka Ringwald 
1371cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1372cf26c8fbSMilanka Ringwald     if (client == NULL) {
1373cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1374cf26c8fbSMilanka Ringwald     }
1375cf26c8fbSMilanka Ringwald 
13766bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1377cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1378cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1379cf26c8fbSMilanka Ringwald 
1380cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1381cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1382cf26c8fbSMilanka Ringwald }
1383cf26c8fbSMilanka Ringwald 
1384cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1385cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1386cf26c8fbSMilanka Ringwald     if (client == NULL){
1387cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1388cf26c8fbSMilanka Ringwald     }
1389cf26c8fbSMilanka Ringwald     // finalize connection
1390cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1391cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1392cf26c8fbSMilanka Ringwald }
1393cf26c8fbSMilanka Ringwald 
1394fd39e93aSMilanka 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){
13951624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
13961624214bSMilanka Ringwald     if (client == NULL){
13971624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
13981624214bSMilanka Ringwald     }
13991624214bSMilanka Ringwald 
14001624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14011624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14021624214bSMilanka Ringwald     }
14031624214bSMilanka Ringwald 
1404fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
140583d7ed1cSMilanka Ringwald 
14061624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14071624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14081624214bSMilanka Ringwald     }
14091624214bSMilanka Ringwald 
14101624214bSMilanka Ringwald     uint16_t mtu;
14111624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14121624214bSMilanka Ringwald 
14131624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14141624214bSMilanka Ringwald         return status;
14151624214bSMilanka Ringwald     }
14161624214bSMilanka Ringwald 
14171624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14181624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14191624214bSMilanka Ringwald     }
14201624214bSMilanka Ringwald 
14216d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1422021192e1SMilanka Ringwald     client->report_index = report_index;
14231624214bSMilanka Ringwald     client->report = report;
14241624214bSMilanka Ringwald     client->report_len = report_len;
14251624214bSMilanka Ringwald 
14261624214bSMilanka Ringwald     hids_run_for_client(client);
14271624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14281624214bSMilanka Ringwald }
14291624214bSMilanka Ringwald 
1430fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
143183d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
143283d7ed1cSMilanka Ringwald     if (client == NULL){
143383d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
143483d7ed1cSMilanka Ringwald     }
143583d7ed1cSMilanka Ringwald 
143683d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
143783d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
143883d7ed1cSMilanka Ringwald     }
143983d7ed1cSMilanka Ringwald 
1440fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
144183d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
144283d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
144383d7ed1cSMilanka Ringwald     }
144483d7ed1cSMilanka Ringwald 
144583d7ed1cSMilanka Ringwald     client->report_index = report_index;
144683d7ed1cSMilanka Ringwald 
144783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
144883d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
144983d7ed1cSMilanka Ringwald #else
145083d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
145183d7ed1cSMilanka Ringwald #endif
145283d7ed1cSMilanka Ringwald     hids_run_for_client(client);
145383d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
145483d7ed1cSMilanka Ringwald }
145583d7ed1cSMilanka Ringwald 
145683d7ed1cSMilanka Ringwald 
1457f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1458f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1459f4d3b82aSMilanka Ringwald     if (client == NULL){
1460f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1461f4d3b82aSMilanka Ringwald     }
1462f4d3b82aSMilanka Ringwald 
1463f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1464f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1465f4d3b82aSMilanka Ringwald     }
1466f4d3b82aSMilanka Ringwald 
1467f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1468f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1469f4d3b82aSMilanka Ringwald     }
1470f4d3b82aSMilanka Ringwald 
1471f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1472af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1473af2241c2SMilanka Ringwald 
1474af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1475af2241c2SMilanka Ringwald     hids_run_for_client(client);
1476af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1477af2241c2SMilanka Ringwald }
1478af2241c2SMilanka Ringwald 
1479af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1480af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1481af2241c2SMilanka Ringwald     if (client == NULL){
1482af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1483af2241c2SMilanka Ringwald     }
1484af2241c2SMilanka Ringwald 
1485af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1486af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1487af2241c2SMilanka Ringwald     }
1488af2241c2SMilanka Ringwald 
1489af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1490af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1491af2241c2SMilanka Ringwald     }
1492af2241c2SMilanka Ringwald 
1493af2241c2SMilanka Ringwald     client->service_index = service_index;
1494af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1495af2241c2SMilanka Ringwald 
1496af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1497f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1498f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1499f4d3b82aSMilanka Ringwald }
1500f4d3b82aSMilanka Ringwald 
15010cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15026d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15036d6f7efcSMilanka Ringwald     if (client == NULL){
15046d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15056d6f7efcSMilanka Ringwald     }
15066d6f7efcSMilanka Ringwald 
15076d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15086d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15096d6f7efcSMilanka Ringwald     }
15106d6f7efcSMilanka Ringwald 
15116d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15126d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15136d6f7efcSMilanka Ringwald     }
15146d6f7efcSMilanka Ringwald 
15156d6f7efcSMilanka Ringwald     client->service_index = service_index;
15166d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15176d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15186d6f7efcSMilanka Ringwald 
15196d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15206d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15216d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15226d6f7efcSMilanka Ringwald }
15236d6f7efcSMilanka Ringwald 
15246d6f7efcSMilanka Ringwald 
15256d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15266d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15276d6f7efcSMilanka Ringwald     if (client == NULL){
15286d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15296d6f7efcSMilanka Ringwald     }
15306d6f7efcSMilanka Ringwald 
15316d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15326d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15336d6f7efcSMilanka Ringwald     }
15346d6f7efcSMilanka Ringwald 
15356d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15366d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15376d6f7efcSMilanka Ringwald     }
15386d6f7efcSMilanka Ringwald 
15396d6f7efcSMilanka Ringwald     client->service_index = service_index;
15406d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15416d6f7efcSMilanka Ringwald     client->value = value;
15426d6f7efcSMilanka Ringwald 
15436d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15446d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15456d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15466d6f7efcSMilanka Ringwald }
15476d6f7efcSMilanka Ringwald 
15486d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15496d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15506d6f7efcSMilanka Ringwald }
15516d6f7efcSMilanka Ringwald 
15526d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
15536d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
15546d6f7efcSMilanka Ringwald }
15556d6f7efcSMilanka Ringwald 
15560cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1557cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1558cd28e7b1SMilanka Ringwald     if (client == NULL){
1559cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1560cd28e7b1SMilanka Ringwald     }
1561cd28e7b1SMilanka Ringwald 
1562cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1563cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1564cd28e7b1SMilanka Ringwald     }
1565cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1566cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1567cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1568cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1569cd28e7b1SMilanka Ringwald     }
1570cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1571cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1572cd28e7b1SMilanka Ringwald }
1573cd28e7b1SMilanka Ringwald 
15740cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1575cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1576cd28e7b1SMilanka Ringwald     if (client == NULL){
1577cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1578cd28e7b1SMilanka Ringwald     }
1579cd28e7b1SMilanka Ringwald 
1580cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1581cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1582cd28e7b1SMilanka Ringwald     }
1583cd28e7b1SMilanka Ringwald 
1584cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1585cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1586cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1587cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1588cd28e7b1SMilanka Ringwald     }
1589cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1590cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1591cd28e7b1SMilanka Ringwald }
15926d6f7efcSMilanka Ringwald 
1593021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1594021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1595021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1596021192e1SMilanka Ringwald }
1597cf26c8fbSMilanka Ringwald 
1598cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1599