xref: /btstack/src/ble/gatt-service/hids_client.c (revision 2fca4dad957cd7b88f4657ed51e89c12615dda72)
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
23*2fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24*2fca4dadSMilanka 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);
72cf26c8fbSMilanka Ringwald 
73556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
74556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
75556456ccSMilanka Ringwald     switch (uuid){
76556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
77556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
78556456ccSMilanka Ringwald 
79556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
80556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
81556456ccSMilanka Ringwald 
82556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
83556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
84556456ccSMilanka Ringwald 
85556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
86556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
87556456ccSMilanka Ringwald 
88556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
89556456ccSMilanka Ringwald             return "REPORT";
90556456ccSMilanka Ringwald 
91556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
92556456ccSMilanka Ringwald             return "REPORT_MAP";
93556456ccSMilanka Ringwald 
94556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
95556456ccSMilanka Ringwald             return "HID_INFORMATION";
96556456ccSMilanka Ringwald 
97556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
98556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
99556456ccSMilanka Ringwald         default:
100556456ccSMilanka Ringwald             return "UKNOWN";
101556456ccSMilanka Ringwald     }
102556456ccSMilanka Ringwald }
103556456ccSMilanka Ringwald #endif
104556456ccSMilanka Ringwald 
105da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
106da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
107da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
108da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
109da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
110da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
111da142a6fSMilanka Ringwald         return client;
112da142a6fSMilanka Ringwald     }
113da142a6fSMilanka Ringwald     return NULL;
114da142a6fSMilanka Ringwald }
115da142a6fSMilanka Ringwald 
116da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
117da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
118da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
119da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
120da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
121da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
122da142a6fSMilanka Ringwald         return client;
123da142a6fSMilanka Ringwald     }
124da142a6fSMilanka Ringwald     return NULL;
125da142a6fSMilanka Ringwald }
126da142a6fSMilanka Ringwald 
127da142a6fSMilanka Ringwald 
128da142a6fSMilanka Ringwald // START Descriptor Storage Util
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
131da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
132da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
133da142a6fSMilanka Ringwald     uint8_t i;
134da142a6fSMilanka Ringwald 
135da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
136da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
137da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
138da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
139da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
140da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
141da142a6fSMilanka Ringwald         }
142da142a6fSMilanka Ringwald     }
143da142a6fSMilanka Ringwald     return free_space;
144da142a6fSMilanka Ringwald }
145da142a6fSMilanka Ringwald 
146da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
147da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
148da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
150da142a6fSMilanka Ringwald }
151da142a6fSMilanka Ringwald 
152da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
153da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
154da142a6fSMilanka Ringwald 
155da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
156da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
157da142a6fSMilanka Ringwald     return true;
158da142a6fSMilanka Ringwald }
159da142a6fSMilanka Ringwald 
160da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
161da142a6fSMilanka Ringwald     uint8_t service_index = 0;
162da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
163da142a6fSMilanka Ringwald 
164da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
165da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
166da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
167da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
168da142a6fSMilanka Ringwald     }
169da142a6fSMilanka Ringwald 
170da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
171da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
172da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
173da142a6fSMilanka Ringwald 
174da142a6fSMilanka Ringwald     uint8_t i;
175da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
176da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
177da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
178da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
179da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
180da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
181da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
182da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
183da142a6fSMilanka Ringwald             }
184da142a6fSMilanka Ringwald         }
185da142a6fSMilanka Ringwald     }
186da142a6fSMilanka Ringwald }
187da142a6fSMilanka Ringwald 
188da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
189da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
190021192e1SMilanka Ringwald     if (client == NULL){
191021192e1SMilanka Ringwald         return NULL;
192021192e1SMilanka Ringwald     }
193021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
194da142a6fSMilanka Ringwald         return NULL;
195da142a6fSMilanka Ringwald     }
196da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
197da142a6fSMilanka Ringwald }
198da142a6fSMilanka Ringwald 
199da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
200da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
201021192e1SMilanka Ringwald     if (client == NULL){
202021192e1SMilanka Ringwald         return 0;
203021192e1SMilanka Ringwald     }
204021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
205da142a6fSMilanka Ringwald         return 0;
206da142a6fSMilanka Ringwald     }
207da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
208da142a6fSMilanka Ringwald }
209da142a6fSMilanka Ringwald 
210da142a6fSMilanka Ringwald // END Descriptor Storage Util
211da142a6fSMilanka Ringwald 
212cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
213cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
214cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
215cf26c8fbSMilanka Ringwald     } else {
216cf26c8fbSMilanka Ringwald         hids_cid_counter++;
217cf26c8fbSMilanka Ringwald     }
218cf26c8fbSMilanka Ringwald     return hids_cid_counter;
219fc975d0eSMilanka Ringwald }
220fc975d0eSMilanka Ringwald 
221ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
222ab116b1cSMilanka Ringwald     uint8_t i;
223a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
224ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
225ab116b1cSMilanka Ringwald             return i;
226ab116b1cSMilanka Ringwald         }
227ab116b1cSMilanka Ringwald     }
228ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
229ab116b1cSMilanka Ringwald }
230ab116b1cSMilanka Ringwald 
23119146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
23219146789SMilanka Ringwald     uint8_t i;
23319146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
23419146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
23519146789SMilanka Ringwald             return i;
23619146789SMilanka Ringwald         }
23719146789SMilanka Ringwald     }
23819146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
23919146789SMilanka Ringwald }
24019146789SMilanka Ringwald 
24119146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
24219146789SMilanka Ringwald     uint8_t i;
24319146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24419146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
24519146789SMilanka Ringwald             return true;
24619146789SMilanka Ringwald         }
24719146789SMilanka Ringwald     }
24819146789SMilanka Ringwald     return false;
24919146789SMilanka Ringwald }
25019146789SMilanka Ringwald 
251fd39e93aSMilanka 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){
25283d7ed1cSMilanka Ringwald     uint8_t i;
253835a13f1SMilanka Ringwald 
25483d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
255fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
256fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
257fd39e93aSMilanka Ringwald 
258fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
25983d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
26083d7ed1cSMilanka Ringwald                 continue;
26183d7ed1cSMilanka Ringwald             }
262fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
26383d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
26483d7ed1cSMilanka Ringwald                 continue;
26583d7ed1cSMilanka Ringwald             }
266fd39e93aSMilanka Ringwald         }
267fd39e93aSMilanka Ringwald 
268fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
26983d7ed1cSMilanka Ringwald             return i;
27083d7ed1cSMilanka Ringwald         }
27183d7ed1cSMilanka Ringwald     }
27283d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
27383d7ed1cSMilanka Ringwald }
27419146789SMilanka Ringwald 
275021192e1SMilanka 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){
276ab116b1cSMilanka Ringwald 
27719146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
278ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2793322b222SMilanka Ringwald         return report_index;
280ab116b1cSMilanka Ringwald     }
28128da36a6SMilanka Ringwald     report_index = client->num_reports;
282ab116b1cSMilanka Ringwald 
28328da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
28428da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
28528da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
28628da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
287ab116b1cSMilanka Ringwald 
28828da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
28928da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
29028da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
291021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
29270093cf5SMilanka Ringwald 
293021192e1SMilanka 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);
294ab116b1cSMilanka Ringwald         client->num_reports++;
2953322b222SMilanka Ringwald         return report_index;
296ab116b1cSMilanka Ringwald     } else {
297ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
2983322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
299ab116b1cSMilanka Ringwald     }
300ab116b1cSMilanka Ringwald }
301ab116b1cSMilanka Ringwald 
30219146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
303556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
304556456ccSMilanka Ringwald 
305556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
30619146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
307556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
30819146789SMilanka Ringwald 
309556456ccSMilanka Ringwald         client->num_external_reports++;
31019146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
311556456ccSMilanka Ringwald         return report_index;
312556456ccSMilanka Ringwald     } else {
313556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
314556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
31570093cf5SMilanka Ringwald     }
316556456ccSMilanka Ringwald }
317556456ccSMilanka Ringwald 
3187e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3197e1e6e7dSMilanka Ringwald     uint8_t i;
3203322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3213322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
322556456ccSMilanka Ringwald             return i;
3233322b222SMilanka Ringwald         }
3243322b222SMilanka Ringwald     }
325556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
326556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3273322b222SMilanka Ringwald }
3283322b222SMilanka Ringwald 
3293322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3303322b222SMilanka Ringwald     client->service_index++;
3313322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
332556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3333322b222SMilanka Ringwald         return true;
3343322b222SMilanka Ringwald     }
3353322b222SMilanka Ringwald     return false;
3363322b222SMilanka Ringwald }
3373322b222SMilanka Ringwald 
3383322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3393322b222SMilanka Ringwald     client->service_index = 0;
3403322b222SMilanka Ringwald 
3413322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3423322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3433322b222SMilanka Ringwald         return true;
3443322b222SMilanka Ringwald     }
3453322b222SMilanka Ringwald     return false;
3463322b222SMilanka Ringwald }
3473322b222SMilanka Ringwald 
3483322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
349021192e1SMilanka Ringwald     client->report_index++;
35019146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3513322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
352e7bd2dbeSMilanka Ringwald         return true;
353e7bd2dbeSMilanka Ringwald     }
354e7bd2dbeSMilanka Ringwald     return false;
355e7bd2dbeSMilanka Ringwald }
356e7bd2dbeSMilanka Ringwald 
3573322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
358021192e1SMilanka Ringwald     client->report_index = 0;
35919146789SMilanka Ringwald     if (client->num_external_reports > 0){
3603322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3617e1e6e7dSMilanka Ringwald         return true;
3627e1e6e7dSMilanka Ringwald     }
3637e1e6e7dSMilanka Ringwald     return false;
3647e1e6e7dSMilanka Ringwald }
3657e1e6e7dSMilanka Ringwald 
3667e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3677e1e6e7dSMilanka Ringwald     uint8_t i;
3687e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
369fd39e93aSMilanka Ringwald 
370021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
37170093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
372fd39e93aSMilanka Ringwald         if (!report.boot_report){
37370093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3747e1e6e7dSMilanka Ringwald                 index = i;
375021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3767e1e6e7dSMilanka Ringwald                 break;
37770093cf5SMilanka Ringwald             }
378fd39e93aSMilanka Ringwald         }
379fd39e93aSMilanka Ringwald     }
380021192e1SMilanka Ringwald     client->report_index = index;
3817e1e6e7dSMilanka Ringwald     return index;
3827e1e6e7dSMilanka Ringwald }
3837e1e6e7dSMilanka Ringwald 
3847e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
385021192e1SMilanka Ringwald     client->report_index++;
3867e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
38728da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
388e7bd2dbeSMilanka Ringwald         return true;
38970093cf5SMilanka Ringwald     }
3907e1e6e7dSMilanka Ringwald     return false;
3917e1e6e7dSMilanka Ringwald }
3927e1e6e7dSMilanka Ringwald 
3937e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
394021192e1SMilanka Ringwald     client->report_index = 0;
3957e1e6e7dSMilanka Ringwald 
3967e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
39728da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
3987e1e6e7dSMilanka Ringwald         return true;
3997e1e6e7dSMilanka Ringwald     }
400e7bd2dbeSMilanka Ringwald     return false;
40170093cf5SMilanka Ringwald }
402ab116b1cSMilanka Ringwald 
403021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
404021192e1SMilanka Ringwald     uint8_t i;
405021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
406021192e1SMilanka Ringwald 
407021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
408021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
409fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
410fd39e93aSMilanka Ringwald 
411fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
412fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
413021192e1SMilanka Ringwald                 continue;
414021192e1SMilanka Ringwald             }
415fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
416fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
417fd39e93aSMilanka Ringwald                 continue;
418fd39e93aSMilanka Ringwald             }
419fd39e93aSMilanka Ringwald         }
420fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
421021192e1SMilanka Ringwald             index = i;
422021192e1SMilanka Ringwald         }
423021192e1SMilanka Ringwald     }
424021192e1SMilanka Ringwald     client->report_index = index;
425021192e1SMilanka Ringwald     return index;
426021192e1SMilanka Ringwald }
427021192e1SMilanka Ringwald 
428021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
429021192e1SMilanka Ringwald     client->report_index++;
430021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
431021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
432021192e1SMilanka Ringwald         return true;
433021192e1SMilanka Ringwald     }
434021192e1SMilanka Ringwald     return false;
435021192e1SMilanka Ringwald }
436021192e1SMilanka Ringwald 
437021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
438cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
439cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
440cd28e7b1SMilanka Ringwald #endif
441021192e1SMilanka Ringwald     client->report_index = 0;
442021192e1SMilanka Ringwald 
443021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
444021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
445021192e1SMilanka Ringwald         return true;
446021192e1SMilanka Ringwald     }
447021192e1SMilanka Ringwald     return false;
448021192e1SMilanka Ringwald }
449021192e1SMilanka Ringwald 
450cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
451cd28e7b1SMilanka Ringwald     client->report_index++;
452cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
453cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
454cd28e7b1SMilanka Ringwald         return true;
455cd28e7b1SMilanka Ringwald     }
456cd28e7b1SMilanka Ringwald     return false;
457cd28e7b1SMilanka Ringwald }
458cd28e7b1SMilanka Ringwald 
459cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
460cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
461cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
462cd28e7b1SMilanka Ringwald #endif
463cd28e7b1SMilanka Ringwald     client->report_index = 0;
464cd28e7b1SMilanka Ringwald 
465cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
466cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
467cd28e7b1SMilanka Ringwald         return true;
468cd28e7b1SMilanka Ringwald     }
469cd28e7b1SMilanka Ringwald     return false;
470cd28e7b1SMilanka Ringwald }
471cd28e7b1SMilanka Ringwald 
472cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
473cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
474cf26c8fbSMilanka Ringwald     if (!client){
475cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
476cf26c8fbSMilanka Ringwald         return NULL;
477cf26c8fbSMilanka Ringwald     }
478cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
479cf26c8fbSMilanka Ringwald     client->cid = cid;
480cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
481fc975d0eSMilanka Ringwald 
482cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
483cf26c8fbSMilanka Ringwald     return client;
484fc975d0eSMilanka Ringwald }
485fc975d0eSMilanka Ringwald 
486cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
487021192e1SMilanka Ringwald     // stop listening
488021192e1SMilanka Ringwald     uint8_t i;
489021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
490021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
491021192e1SMilanka Ringwald     }
492021192e1SMilanka Ringwald 
493da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
494cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
495cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
496fc975d0eSMilanka Ringwald }
497cf26c8fbSMilanka Ringwald 
498cf26c8fbSMilanka Ringwald 
499cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5006bcfb631SMilanka Ringwald     uint8_t event[8];
501cf26c8fbSMilanka Ringwald     int pos = 0;
502cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
503cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
504cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
505cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
506cf26c8fbSMilanka Ringwald     pos += 2;
507cf26c8fbSMilanka Ringwald     event[pos++] = status;
508fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
509cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
510cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
511cf26c8fbSMilanka Ringwald }
512cf26c8fbSMilanka Ringwald 
513cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
514cd28e7b1SMilanka Ringwald     uint8_t event[6];
515cd28e7b1SMilanka Ringwald     int pos = 0;
516cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
517cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
518cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
519cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
520cd28e7b1SMilanka Ringwald     pos += 2;
521cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
522cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
523cd28e7b1SMilanka Ringwald }
524cd28e7b1SMilanka Ringwald 
525021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
526021192e1SMilanka Ringwald     uint16_t pos = 0;
527021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
528021192e1SMilanka Ringwald     pos++;  // skip len
529021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
530021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
531021192e1SMilanka Ringwald     pos += 2;
532021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
533021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
534021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
535021192e1SMilanka Ringwald     pos += 2;
536021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
53783d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
53883d7ed1cSMilanka Ringwald 
539021192e1SMilanka Ringwald }
540021192e1SMilanka Ringwald 
541f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
542f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
543f4d3b82aSMilanka Ringwald 
544f4d3b82aSMilanka Ringwald     uint8_t event[11];
545f4d3b82aSMilanka Ringwald     int pos = 0;
546f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
547f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
548f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
549f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
550f4d3b82aSMilanka Ringwald     pos += 2;
551f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
552f4d3b82aSMilanka Ringwald 
553f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
554f4d3b82aSMilanka Ringwald     pos += 3;
555f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
556f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
557f4d3b82aSMilanka Ringwald 
558f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
559f4d3b82aSMilanka Ringwald }
560f4d3b82aSMilanka Ringwald 
561af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
562af2241c2SMilanka Ringwald     if (value_len != 1) return;
563af2241c2SMilanka Ringwald 
564af2241c2SMilanka Ringwald     uint8_t event[11];
565af2241c2SMilanka Ringwald     int pos = 0;
566af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
567af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
568af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
569af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
570af2241c2SMilanka Ringwald     pos += 2;
571af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
572af2241c2SMilanka Ringwald     event[pos++] = value[0];
573af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
574af2241c2SMilanka Ringwald }
575f4d3b82aSMilanka Ringwald 
57683d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
577021192e1SMilanka Ringwald     UNUSED(packet_type);
578021192e1SMilanka Ringwald     UNUSED(channel);
57983d7ed1cSMilanka Ringwald 
580021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
581021192e1SMilanka Ringwald 
582021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
583021192e1SMilanka Ringwald     btstack_assert(client != NULL);
584021192e1SMilanka Ringwald 
585021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
586021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
587021192e1SMilanka Ringwald         return;
588021192e1SMilanka Ringwald     }
589021192e1SMilanka Ringwald 
59083d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
591021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
59283d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
593021192e1SMilanka Ringwald }
594021192e1SMilanka Ringwald 
59583d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
59683d7ed1cSMilanka Ringwald     UNUSED(packet_type);
59783d7ed1cSMilanka Ringwald     UNUSED(channel);
59883d7ed1cSMilanka Ringwald 
59983d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
60083d7ed1cSMilanka Ringwald 
60183d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
60283d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
60383d7ed1cSMilanka Ringwald 
60483d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
60583d7ed1cSMilanka Ringwald         return;
60683d7ed1cSMilanka Ringwald     }
60783d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
60883d7ed1cSMilanka Ringwald 
60983d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
61083d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
61183d7ed1cSMilanka Ringwald         return;
61283d7ed1cSMilanka Ringwald     }
61383d7ed1cSMilanka Ringwald 
61483d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
61583d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
61683d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
61783d7ed1cSMilanka Ringwald }
618cf26c8fbSMilanka Ringwald 
619cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
620cf26c8fbSMilanka Ringwald     uint8_t att_status;
6216bcfb631SMilanka Ringwald     gatt_client_service_t service;
6226bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
623cf26c8fbSMilanka Ringwald 
624cf26c8fbSMilanka Ringwald     switch (client->state){
625cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
626556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
627556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
628556456ccSMilanka Ringwald #endif
629cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
63019146789SMilanka Ringwald 
63119146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
632cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
633cf26c8fbSMilanka Ringwald             UNUSED(att_status);
634cf26c8fbSMilanka Ringwald             break;
635cf26c8fbSMilanka Ringwald 
636cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
637556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
638556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
639556456ccSMilanka Ringwald #endif
6406bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6416bcfb631SMilanka Ringwald 
6426bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6436bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
64419146789SMilanka Ringwald 
64519146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6466bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6476bcfb631SMilanka Ringwald 
6486bcfb631SMilanka Ringwald             UNUSED(att_status);
6496bcfb631SMilanka Ringwald             break;
6506bcfb631SMilanka Ringwald 
65128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
652556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
653556456ccSMilanka 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);
654556456ccSMilanka Ringwald #endif
65528da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
656556456ccSMilanka Ringwald 
65719146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
65819146789SMilanka 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);
659e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
660e7bd2dbeSMilanka Ringwald             break;
661e7bd2dbeSMilanka Ringwald 
662e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
663556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
664556456ccSMilanka 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);
665556456ccSMilanka Ringwald #endif
666e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
667e7bd2dbeSMilanka Ringwald 
6683322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6693322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6703322b222SMilanka Ringwald 
67119146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
672e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
673e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
674e7bd2dbeSMilanka Ringwald             break;
675e7bd2dbeSMilanka Ringwald 
67628da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
677556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
67819146789SMilanka 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);
679556456ccSMilanka Ringwald #endif
68028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
681e7bd2dbeSMilanka Ringwald 
68219146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
68319146789SMilanka 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);
684e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
685e7bd2dbeSMilanka Ringwald             break;
686e7bd2dbeSMilanka Ringwald 
6873322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
688556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
68919146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
690556456ccSMilanka Ringwald #endif
6913322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
692e7bd2dbeSMilanka Ringwald 
6933322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
6943322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
69519146789SMilanka Ringwald 
69619146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6973322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
698e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
699e7bd2dbeSMilanka Ringwald             break;
700e7bd2dbeSMilanka Ringwald 
70128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
702556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
70383d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
704556456ccSMilanka Ringwald                 client->report_index,
70583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
70683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
707556456ccSMilanka Ringwald #endif
70828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
709af2241c2SMilanka Ringwald             client->handle = 0;
710556456ccSMilanka Ringwald 
711556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
712556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
713556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
714556456ccSMilanka Ringwald 
71519146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
71670093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
71770093cf5SMilanka Ringwald             UNUSED(att_status);
71870093cf5SMilanka Ringwald             break;
71970093cf5SMilanka Ringwald 
72028da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
72128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
72270093cf5SMilanka Ringwald 
72319146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
724af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
725af2241c2SMilanka Ringwald             client->handle = 0;
72670093cf5SMilanka Ringwald             UNUSED(att_status);
72770093cf5SMilanka Ringwald             break;
72870093cf5SMilanka Ringwald 
729cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
730cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
731cd28e7b1SMilanka Ringwald             if (client->value > 0){
732cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
733cd28e7b1SMilanka Ringwald             } else {
734cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
735cd28e7b1SMilanka Ringwald             }
736cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
737cd28e7b1SMilanka Ringwald                 client->report_index,
738cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
739cd28e7b1SMilanka Ringwald #endif
740cd28e7b1SMilanka Ringwald 
741cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
742cd28e7b1SMilanka Ringwald 
743cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
744cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
745cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
746cd28e7b1SMilanka Ringwald 
747cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
748cd28e7b1SMilanka Ringwald 
749cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
750cd28e7b1SMilanka Ringwald 
751cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
752cd28e7b1SMilanka Ringwald                 switch(client->value){
753cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
754cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
755cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
756cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
757cd28e7b1SMilanka Ringwald                         break;
758cd28e7b1SMilanka Ringwald                     default:
759cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
760cd28e7b1SMilanka Ringwald                         break;
761cd28e7b1SMilanka Ringwald                 }
762cd28e7b1SMilanka Ringwald             } else {
763cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
764cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
765cd28e7b1SMilanka Ringwald                     break;
766cd28e7b1SMilanka Ringwald                 }
767cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
768cd28e7b1SMilanka Ringwald             }
769cd28e7b1SMilanka Ringwald             break;
770cd28e7b1SMilanka Ringwald 
771021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
772cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
773cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
774cd28e7b1SMilanka Ringwald                 client->report_index,
775cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
776cd28e7b1SMilanka Ringwald #endif
777021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
778021192e1SMilanka Ringwald 
779021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
780021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
781021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
782021192e1SMilanka Ringwald 
78319146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
784021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
785021192e1SMilanka Ringwald 
786cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
787cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
788cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
789cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
790cd28e7b1SMilanka Ringwald             } else {
791021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
792021192e1SMilanka Ringwald                     hids_run_for_client(client);
793021192e1SMilanka Ringwald                     break;
794021192e1SMilanka Ringwald                 }
795021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
796021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
797021192e1SMilanka Ringwald             }
798021192e1SMilanka Ringwald             break;
799021192e1SMilanka Ringwald 
80083d7ed1cSMilanka Ringwald 
8016d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
80283d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8036d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
80483d7ed1cSMilanka Ringwald                 client->report_index,
80583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
80683d7ed1cSMilanka Ringwald #endif
80783d7ed1cSMilanka Ringwald 
8086d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
80983d7ed1cSMilanka Ringwald 
8106d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8116d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
8126d6f7efcSMilanka Ringwald                 &handle_report_event, client->con_handle,
81383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
81483d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
81583d7ed1cSMilanka Ringwald             UNUSED(att_status);
81683d7ed1cSMilanka Ringwald             break;
81783d7ed1cSMilanka Ringwald 
81883d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
81983d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
820f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
821f4d3b82aSMilanka Ringwald                 client->report_index,
82283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
82383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82483d7ed1cSMilanka Ringwald #endif
82583d7ed1cSMilanka Ringwald 
82683d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
827f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
82883d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
82983d7ed1cSMilanka Ringwald                 &handle_report_event,
83083d7ed1cSMilanka Ringwald                 client->con_handle,
83183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
83283d7ed1cSMilanka Ringwald             UNUSED(att_status);
83383d7ed1cSMilanka Ringwald             break;
83483d7ed1cSMilanka Ringwald 
83583d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
83683d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
83783d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
83883d7ed1cSMilanka Ringwald 
839f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
84083d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
84183d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
84283d7ed1cSMilanka Ringwald                 client->con_handle,
84383d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
84483d7ed1cSMilanka Ringwald 
84583d7ed1cSMilanka Ringwald             break;
84683d7ed1cSMilanka Ringwald #endif
847af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
848af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
84983d7ed1cSMilanka Ringwald 
850f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
851f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
852f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
853f4d3b82aSMilanka Ringwald                 client->con_handle,
854af2241c2SMilanka Ringwald                 client->handle);
855f4d3b82aSMilanka Ringwald             break;
8566d6f7efcSMilanka Ringwald 
857fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8586d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
859fd39e93aSMilanka Ringwald             (void) gatt_client_request_can_write_without_response_event(&handle_gatt_client_event, client->con_handle);
8606d6f7efcSMilanka Ringwald             break;
8616d6f7efcSMilanka Ringwald 
8626bcfb631SMilanka Ringwald         default:
8636bcfb631SMilanka Ringwald             break;
8646bcfb631SMilanka Ringwald     }
8656bcfb631SMilanka Ringwald }
8666bcfb631SMilanka Ringwald 
867cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
868cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
869cf26c8fbSMilanka Ringwald     UNUSED(channel);
870cf26c8fbSMilanka Ringwald     UNUSED(size);
871cf26c8fbSMilanka Ringwald 
8726bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
873cf26c8fbSMilanka Ringwald     uint8_t att_status;
8746bcfb631SMilanka Ringwald     gatt_client_service_t service;
8756bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
87670093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
877ab116b1cSMilanka Ringwald 
878021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
879021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
880e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
8813322b222SMilanka Ringwald     uint8_t i;
8823322b222SMilanka Ringwald     uint8_t report_index;
883da142a6fSMilanka Ringwald 
884f4d3b82aSMilanka Ringwald     const uint8_t * value;
885f4d3b82aSMilanka Ringwald     uint16_t value_len;
886cf26c8fbSMilanka Ringwald 
887cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
888cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
889cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
890cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
891cf26c8fbSMilanka Ringwald 
892021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
893021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
8946bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
895021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
896021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
8976bcfb631SMilanka Ringwald                 client->num_instances++;
898021192e1SMilanka Ringwald 
899708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
900708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
901708c69d2SMilanka Ringwald #endif
902021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
903021192e1SMilanka Ringwald             }  else {
904021192e1SMilanka 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);
905da142a6fSMilanka Ringwald             }
9066bcfb631SMilanka Ringwald             break;
9076bcfb631SMilanka Ringwald 
9086bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9096bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
9106bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
9116bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
91219146789SMilanka Ringwald 
91319146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
91419146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
91519146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
91619146789SMilanka Ringwald                     break;
91719146789SMilanka Ringwald                 }
91819146789SMilanka Ringwald             }
91919146789SMilanka Ringwald 
92019146789SMilanka Ringwald             switch (characteristic.uuid16){
92119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
922af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
92319146789SMilanka Ringwald                     break;
92419146789SMilanka Ringwald 
92519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
92619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
92719146789SMilanka Ringwald                     break;
92819146789SMilanka Ringwald 
92919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
93019146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
93119146789SMilanka Ringwald                     break;
93219146789SMilanka Ringwald 
93319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
934f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
93519146789SMilanka Ringwald                     break;
93619146789SMilanka Ringwald 
93719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
93819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
93919146789SMilanka Ringwald                     break;
94019146789SMilanka Ringwald 
94119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94219146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
94319146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
94419146789SMilanka Ringwald                     break;
94519146789SMilanka Ringwald 
94619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
947f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
94819146789SMilanka Ringwald                     break;
94919146789SMilanka Ringwald 
95019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
951f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
95219146789SMilanka Ringwald                     break;
95319146789SMilanka Ringwald 
95419146789SMilanka Ringwald                 default:
95519146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
95619146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
95719146789SMilanka Ringwald #endif
95819146789SMilanka Ringwald                     return;
95919146789SMilanka Ringwald             }
96019146789SMilanka Ringwald 
96119146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
96219146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
96319146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
96419146789SMilanka Ringwald                 characteristic.start_handle,
96519146789SMilanka Ringwald                 characteristic.properties,
96619146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
96719146789SMilanka Ringwald                 client->service_index);
96819146789SMilanka Ringwald 
96919146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
97019146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
97119146789SMilanka Ringwald             }
97219146789SMilanka Ringwald             printf("\n");
97319146789SMilanka Ringwald #endif
974cf26c8fbSMilanka Ringwald             break;
975cf26c8fbSMilanka Ringwald 
9768cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
977e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
9788cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
979e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
980da142a6fSMilanka Ringwald 
981f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
982f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
9838cec2b74SMilanka Ringwald 
984556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9858cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
986f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
987556456ccSMilanka Ringwald #endif
988f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
989f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
990da142a6fSMilanka Ringwald                 if (!stored){
991da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
992da142a6fSMilanka Ringwald                     break;
993da142a6fSMilanka Ringwald                 }
994da142a6fSMilanka Ringwald             }
995e7bd2dbeSMilanka Ringwald             break;
996e7bd2dbeSMilanka Ringwald 
99770093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
99870093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
99970093cf5SMilanka Ringwald             btstack_assert(client != NULL);
100070093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
100170093cf5SMilanka Ringwald 
1002e7bd2dbeSMilanka Ringwald             switch (client->state) {
1003e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1004e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1005e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
100619146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10073322b222SMilanka Ringwald 
1008556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1009556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1010556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1011556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1012556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1013556456ccSMilanka Ringwald                                 client->service_index, report_index);
1014556456ccSMilanka Ringwald                         }
1015556456ccSMilanka Ringwald #endif
1016e7bd2dbeSMilanka Ringwald                     }
1017e7bd2dbeSMilanka Ringwald                     break;
101828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
101970093cf5SMilanka Ringwald                     // setup for descriptor value query
102070093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1021af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1022556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1023556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1024556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1025556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
102619146789SMilanka Ringwald #endif
1027556456ccSMilanka Ringwald                     }
1028556456ccSMilanka Ringwald 
102919146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1030556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
103183d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1032556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1033556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1034556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1035556456ccSMilanka Ringwald                     }
1036556456ccSMilanka Ringwald #endif
103770093cf5SMilanka Ringwald                     break;
1038556456ccSMilanka Ringwald 
1039e7bd2dbeSMilanka Ringwald                 default:
1040e7bd2dbeSMilanka Ringwald                     break;
1041e7bd2dbeSMilanka Ringwald             }
1042e7bd2dbeSMilanka Ringwald             break;
104370093cf5SMilanka Ringwald 
104483d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
104583d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
104683d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
104783d7ed1cSMilanka Ringwald 
1048f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1049f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1050f4d3b82aSMilanka Ringwald 
1051af2241c2SMilanka Ringwald 
1052f4d3b82aSMilanka Ringwald             switch (client->state){
1053f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1054f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
105583d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1056f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
105783d7ed1cSMilanka Ringwald                     break;
105883d7ed1cSMilanka Ringwald #endif
1059af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1060af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1061af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1062f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1063f4d3b82aSMilanka Ringwald                         break;
1064af2241c2SMilanka Ringwald                     }
1065af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1066af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1067af2241c2SMilanka Ringwald                         break;
1068af2241c2SMilanka Ringwald                     }
1069af2241c2SMilanka Ringwald                     break;
1070af2241c2SMilanka Ringwald                 }
1071f4d3b82aSMilanka Ringwald                 default:
1072f4d3b82aSMilanka Ringwald                     break;
1073f4d3b82aSMilanka Ringwald             }
1074f4d3b82aSMilanka Ringwald 
1075f4d3b82aSMilanka Ringwald             break;
107683d7ed1cSMilanka Ringwald 
107770093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
107870093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
107970093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1080e7bd2dbeSMilanka Ringwald 
1081e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1082e7bd2dbeSMilanka Ringwald                 break;
1083e7bd2dbeSMilanka Ringwald             }
10847e1e6e7dSMilanka Ringwald 
1085e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1086e7bd2dbeSMilanka Ringwald             switch (client->state) {
108728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1088e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
108919146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
10903322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
109119146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1092556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
109319146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
109419146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1095556456ccSMilanka Ringwald #endif
10963322b222SMilanka Ringwald                     }
1097e7bd2dbeSMilanka Ringwald                     break;
1098e7bd2dbeSMilanka Ringwald 
109928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
110019146789SMilanka Ringwald 
110119146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1102021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1103021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
110419146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
110583d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1106021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1107021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
110883d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
110919146789SMilanka Ringwald     #endif
111019146789SMilanka Ringwald                     }
1111e7bd2dbeSMilanka Ringwald                     break;
1112e7bd2dbeSMilanka Ringwald 
1113e7bd2dbeSMilanka Ringwald                 default:
1114e7bd2dbeSMilanka Ringwald                     break;
111570093cf5SMilanka Ringwald             }
111670093cf5SMilanka Ringwald             break;
111770093cf5SMilanka Ringwald 
1118fd39e93aSMilanka Ringwald         case GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE:
1119fd39e93aSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_can_write_without_response_get_handle(packet));
1120fd39e93aSMilanka Ringwald             btstack_assert(client != NULL);
1121fd39e93aSMilanka Ringwald 
1122fd39e93aSMilanka Ringwald             switch (client->state){
1123fd39e93aSMilanka Ringwald                 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
1124fd39e93aSMilanka Ringwald                     att_status = gatt_client_write_value_of_characteristic_without_response(
1125fd39e93aSMilanka Ringwald                         client->con_handle,
1126fd39e93aSMilanka Ringwald                         client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
1127fd39e93aSMilanka Ringwald 
1128fd39e93aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1129fd39e93aSMilanka Ringwald                     printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
1130fd39e93aSMilanka Ringwald #endif
1131fd39e93aSMilanka Ringwald 
1132fd39e93aSMilanka Ringwald                     if (att_status == ATT_ERROR_SUCCESS){
1133fd39e93aSMilanka Ringwald                         client->services[client->service_index].protocol_mode = client->required_protocol_mode;
1134fd39e93aSMilanka Ringwald                         if ((client->service_index + 1) < client->num_instances){
1135fd39e93aSMilanka Ringwald                             client->service_index++;
1136fd39e93aSMilanka Ringwald                             hids_run_for_client(client);
1137fd39e93aSMilanka Ringwald                             break;
1138fd39e93aSMilanka Ringwald                         }
1139fd39e93aSMilanka Ringwald                     }
1140fd39e93aSMilanka Ringwald 
1141fd39e93aSMilanka Ringwald                     // read UUIDS for external characteristics
1142fd39e93aSMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1143fd39e93aSMilanka Ringwald                         hids_run_for_client(client);
1144fd39e93aSMilanka Ringwald                         break;
1145fd39e93aSMilanka Ringwald                     }
1146fd39e93aSMilanka Ringwald 
1147fd39e93aSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1148fd39e93aSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
1149fd39e93aSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1150fd39e93aSMilanka Ringwald                         hids_run_for_client(client);
1151fd39e93aSMilanka Ringwald                         break;
1152fd39e93aSMilanka Ringwald                     }
1153fd39e93aSMilanka Ringwald 
1154fd39e93aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1155fd39e93aSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1156fd39e93aSMilanka Ringwald                     break;
1157fd39e93aSMilanka Ringwald 
1158fd39e93aSMilanka Ringwald                 case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
1159fd39e93aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1160fd39e93aSMilanka Ringwald                     printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
1161fd39e93aSMilanka Ringwald #endif
1162fd39e93aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1163fd39e93aSMilanka Ringwald                     (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
1164fd39e93aSMilanka Ringwald                     break;
1165fd39e93aSMilanka Ringwald 
1166fd39e93aSMilanka Ringwald                 default:
1167fd39e93aSMilanka Ringwald                     break;
1168fd39e93aSMilanka Ringwald             }
1169fd39e93aSMilanka Ringwald 
1170fd39e93aSMilanka Ringwald             break;
1171fd39e93aSMilanka Ringwald 
1172cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1173cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1174cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1175cf26c8fbSMilanka Ringwald 
1176cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1177cf26c8fbSMilanka Ringwald 
1178cf26c8fbSMilanka Ringwald             switch (client->state){
1179cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
11806bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11816bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11826bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11835fa2c39aSMilanka Ringwald                         return;
11846bcfb631SMilanka Ringwald                     }
11856bcfb631SMilanka Ringwald 
11866bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
11876bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
11886bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11895fa2c39aSMilanka Ringwald                         return;
11906bcfb631SMilanka Ringwald                     }
11916bcfb631SMilanka Ringwald 
11926bcfb631SMilanka Ringwald                     client->service_index = 0;
119319146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
11946bcfb631SMilanka Ringwald                     break;
11956bcfb631SMilanka Ringwald 
11966bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
11976bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11986bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11996bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12005fa2c39aSMilanka Ringwald                         return;
12016bcfb631SMilanka Ringwald                     }
12026bcfb631SMilanka Ringwald 
120370093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
120470093cf5SMilanka Ringwald                         // discover characteristics of next service
120570093cf5SMilanka Ringwald                         client->service_index++;
120670093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1207021192e1SMilanka Ringwald                         break;
1208021192e1SMilanka Ringwald                     }
1209021192e1SMilanka Ringwald 
1210fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1211fd39e93aSMilanka Ringwald 
1212021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1213021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1214fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1215fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1216021192e1SMilanka Ringwald                             }
12177e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12187e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12197e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
122070093cf5SMilanka Ringwald                                 break;
122170093cf5SMilanka Ringwald                             }
122270093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
122370093cf5SMilanka Ringwald                             hids_finalize_client(client);
1224fd39e93aSMilanka Ringwald                             return;
1225fd39e93aSMilanka Ringwald 
1226fd39e93aSMilanka Ringwald                         default:
1227fd39e93aSMilanka Ringwald                             // set boot mode
1228fd39e93aSMilanka Ringwald                             client->service_index = 0;
1229fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1230fd39e93aSMilanka Ringwald                             break;
1231fd39e93aSMilanka Ringwald                     }
12326bcfb631SMilanka Ringwald                     break;
12336bcfb631SMilanka Ringwald 
12343322b222SMilanka Ringwald 
123528da36a6SMilanka Ringwald                 // HID descriptor found
123628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1237e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1238e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1239e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12405fa2c39aSMilanka Ringwald                         return;
1241e7bd2dbeSMilanka Ringwald                     }
1242e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1243e7bd2dbeSMilanka Ringwald                     break;
1244e7bd2dbeSMilanka Ringwald 
124528da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1246e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12473322b222SMilanka Ringwald                     // go for next report map
12483322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1249e7bd2dbeSMilanka Ringwald                         break;
1250e7bd2dbeSMilanka Ringwald                     }
1251e7bd2dbeSMilanka Ringwald 
12523322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12533322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1254e7bd2dbeSMilanka Ringwald                         break;
1255e7bd2dbeSMilanka Ringwald                     }
1256e7bd2dbeSMilanka Ringwald 
1257e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1258e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12597e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1260e7bd2dbeSMilanka Ringwald                         break;
1261e7bd2dbeSMilanka Ringwald                     }
1262e7bd2dbeSMilanka Ringwald 
1263e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1264e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
12655fa2c39aSMilanka Ringwald                     return;
1266e7bd2dbeSMilanka Ringwald 
126728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1268e7bd2dbeSMilanka Ringwald                     // go for next map report
12693322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1270e7bd2dbeSMilanka Ringwald                         break;
1271e7bd2dbeSMilanka Ringwald                     }
1272e7bd2dbeSMilanka Ringwald 
12733322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12743322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1275e7bd2dbeSMilanka Ringwald                     break;
1276e7bd2dbeSMilanka Ringwald 
12773322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1278e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1279e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12807e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1281e7bd2dbeSMilanka Ringwald                         break;
1282e7bd2dbeSMilanka Ringwald                     }
1283e7bd2dbeSMilanka Ringwald 
1284e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1285e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
12865fa2c39aSMilanka Ringwald                     return;
1287e7bd2dbeSMilanka Ringwald 
128828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1289af2241c2SMilanka Ringwald                     if (client->handle != 0){
129028da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
129170093cf5SMilanka Ringwald                         break;
129270093cf5SMilanka Ringwald                     }
129370093cf5SMilanka Ringwald                     // go for next report
12947e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
129570093cf5SMilanka Ringwald                         break;
129670093cf5SMilanka Ringwald                     }
1297835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
129870093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
129970093cf5SMilanka Ringwald                     break;
130070093cf5SMilanka Ringwald 
130128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
130270093cf5SMilanka Ringwald                     // go for next report
13037e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
130470093cf5SMilanka Ringwald                         break;
130570093cf5SMilanka Ringwald                     }
1306021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1307021192e1SMilanka Ringwald                         break;
1308021192e1SMilanka Ringwald                     }
1309835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
131070093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
131170093cf5SMilanka Ringwald                     break;
131270093cf5SMilanka Ringwald 
1313021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1314021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13152901a9b7SMilanka Ringwald                         break;
13162901a9b7SMilanka Ringwald                     }
1317835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13186bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13196bcfb631SMilanka Ringwald                     break;
1320f4d3b82aSMilanka Ringwald 
1321cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1322cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1323cd28e7b1SMilanka Ringwald                         break;
1324cd28e7b1SMilanka Ringwald                     }
1325cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1326cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1327cd28e7b1SMilanka Ringwald                     break;
1328cd28e7b1SMilanka Ringwald 
1329835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1330835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1331835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1332835a13f1SMilanka Ringwald                     break;
1333835a13f1SMilanka Ringwald #endif
1334f4d3b82aSMilanka Ringwald 
1335af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
13366d6f7efcSMilanka Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1337f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1338f4d3b82aSMilanka Ringwald                     break;
1339f4d3b82aSMilanka Ringwald 
13406d6f7efcSMilanka Ringwald 
1341cf26c8fbSMilanka Ringwald                 default:
1342cf26c8fbSMilanka Ringwald                     break;
1343cf26c8fbSMilanka Ringwald             }
1344cf26c8fbSMilanka Ringwald             break;
1345cf26c8fbSMilanka Ringwald 
1346cf26c8fbSMilanka Ringwald         default:
1347cf26c8fbSMilanka Ringwald             break;
1348cf26c8fbSMilanka Ringwald     }
13496bcfb631SMilanka Ringwald 
13506bcfb631SMilanka Ringwald     if (client != NULL){
13516bcfb631SMilanka Ringwald         hids_run_for_client(client);
13526bcfb631SMilanka Ringwald     }
1353cf26c8fbSMilanka Ringwald }
1354cf26c8fbSMilanka Ringwald 
13556bcfb631SMilanka 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){
1356cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1357cf26c8fbSMilanka Ringwald 
1358cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1359cf26c8fbSMilanka Ringwald     if (client != NULL){
1360cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1361cf26c8fbSMilanka Ringwald     }
1362cf26c8fbSMilanka Ringwald 
1363cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1364cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1365cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1366cf26c8fbSMilanka Ringwald     }
1367cf26c8fbSMilanka Ringwald 
1368cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1369cf26c8fbSMilanka Ringwald     if (client == NULL) {
1370cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1371cf26c8fbSMilanka Ringwald     }
1372cf26c8fbSMilanka Ringwald 
13736bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1374cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1375cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1376cf26c8fbSMilanka Ringwald 
1377cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1378cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1379cf26c8fbSMilanka Ringwald }
1380cf26c8fbSMilanka Ringwald 
1381cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1382cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1383cf26c8fbSMilanka Ringwald     if (client == NULL){
1384cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1385cf26c8fbSMilanka Ringwald     }
1386cf26c8fbSMilanka Ringwald     // finalize connection
1387cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1388cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1389cf26c8fbSMilanka Ringwald }
1390cf26c8fbSMilanka Ringwald 
1391fd39e93aSMilanka 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){
13921624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
13931624214bSMilanka Ringwald     if (client == NULL){
13941624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
13951624214bSMilanka Ringwald     }
13961624214bSMilanka Ringwald 
13971624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
13981624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
13991624214bSMilanka Ringwald     }
14001624214bSMilanka Ringwald 
1401fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
140283d7ed1cSMilanka Ringwald 
14031624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14041624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14051624214bSMilanka Ringwald     }
14061624214bSMilanka Ringwald 
14071624214bSMilanka Ringwald     uint16_t mtu;
14081624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14091624214bSMilanka Ringwald 
14101624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14111624214bSMilanka Ringwald         return status;
14121624214bSMilanka Ringwald     }
14131624214bSMilanka Ringwald 
14141624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14151624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14161624214bSMilanka Ringwald     }
14171624214bSMilanka Ringwald 
14186d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1419021192e1SMilanka Ringwald     client->report_index = report_index;
14201624214bSMilanka Ringwald     client->report = report;
14211624214bSMilanka Ringwald     client->report_len = report_len;
14221624214bSMilanka Ringwald 
14231624214bSMilanka Ringwald     hids_run_for_client(client);
14241624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14251624214bSMilanka Ringwald }
14261624214bSMilanka Ringwald 
1427fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
142883d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
142983d7ed1cSMilanka Ringwald     if (client == NULL){
143083d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
143183d7ed1cSMilanka Ringwald     }
143283d7ed1cSMilanka Ringwald 
143383d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
143483d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
143583d7ed1cSMilanka Ringwald     }
143683d7ed1cSMilanka Ringwald 
1437fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
143883d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
143983d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
144083d7ed1cSMilanka Ringwald     }
144183d7ed1cSMilanka Ringwald 
144283d7ed1cSMilanka Ringwald     client->report_index = report_index;
144383d7ed1cSMilanka Ringwald 
144483d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
144583d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
144683d7ed1cSMilanka Ringwald #else
144783d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
144883d7ed1cSMilanka Ringwald #endif
144983d7ed1cSMilanka Ringwald     hids_run_for_client(client);
145083d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
145183d7ed1cSMilanka Ringwald }
145283d7ed1cSMilanka Ringwald 
145383d7ed1cSMilanka Ringwald 
1454f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1455f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1456f4d3b82aSMilanka Ringwald     if (client == NULL){
1457f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1458f4d3b82aSMilanka Ringwald     }
1459f4d3b82aSMilanka Ringwald 
1460f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1461f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1462f4d3b82aSMilanka Ringwald     }
1463f4d3b82aSMilanka Ringwald 
1464f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1465f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1466f4d3b82aSMilanka Ringwald     }
1467f4d3b82aSMilanka Ringwald 
1468f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1469af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1470af2241c2SMilanka Ringwald 
1471af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1472af2241c2SMilanka Ringwald     hids_run_for_client(client);
1473af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1474af2241c2SMilanka Ringwald }
1475af2241c2SMilanka Ringwald 
1476af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1477af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1478af2241c2SMilanka Ringwald     if (client == NULL){
1479af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1480af2241c2SMilanka Ringwald     }
1481af2241c2SMilanka Ringwald 
1482af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1483af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1484af2241c2SMilanka Ringwald     }
1485af2241c2SMilanka Ringwald 
1486af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1487af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1488af2241c2SMilanka Ringwald     }
1489af2241c2SMilanka Ringwald 
1490af2241c2SMilanka Ringwald     client->service_index = service_index;
1491af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1492af2241c2SMilanka Ringwald 
1493af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1494f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1495f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1496f4d3b82aSMilanka Ringwald }
1497f4d3b82aSMilanka Ringwald 
14980cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
14996d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15006d6f7efcSMilanka Ringwald     if (client == NULL){
15016d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15026d6f7efcSMilanka Ringwald     }
15036d6f7efcSMilanka Ringwald 
15046d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15056d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15066d6f7efcSMilanka Ringwald     }
15076d6f7efcSMilanka Ringwald 
15086d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15096d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15106d6f7efcSMilanka Ringwald     }
15116d6f7efcSMilanka Ringwald 
15126d6f7efcSMilanka Ringwald     client->service_index = service_index;
15136d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15146d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15156d6f7efcSMilanka Ringwald 
15166d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15176d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15186d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15196d6f7efcSMilanka Ringwald }
15206d6f7efcSMilanka Ringwald 
15216d6f7efcSMilanka Ringwald 
15226d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15236d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15246d6f7efcSMilanka Ringwald     if (client == NULL){
15256d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15266d6f7efcSMilanka Ringwald     }
15276d6f7efcSMilanka Ringwald 
15286d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15296d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15306d6f7efcSMilanka Ringwald     }
15316d6f7efcSMilanka Ringwald 
15326d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15336d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15346d6f7efcSMilanka Ringwald     }
15356d6f7efcSMilanka Ringwald 
15366d6f7efcSMilanka Ringwald     client->service_index = service_index;
15376d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15386d6f7efcSMilanka Ringwald     client->value = value;
15396d6f7efcSMilanka Ringwald 
15406d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15416d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15426d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15436d6f7efcSMilanka Ringwald }
15446d6f7efcSMilanka Ringwald 
15456d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15466d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15476d6f7efcSMilanka Ringwald }
15486d6f7efcSMilanka Ringwald 
15496d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
15506d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
15516d6f7efcSMilanka Ringwald }
15526d6f7efcSMilanka Ringwald 
15530cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1554cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1555cd28e7b1SMilanka Ringwald     if (client == NULL){
1556cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1557cd28e7b1SMilanka Ringwald     }
1558cd28e7b1SMilanka Ringwald 
1559cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1560cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1561cd28e7b1SMilanka Ringwald     }
1562cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1563cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1564cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1565cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1566cd28e7b1SMilanka Ringwald     }
1567cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1568cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1569cd28e7b1SMilanka Ringwald }
1570cd28e7b1SMilanka Ringwald 
15710cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1572cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1573cd28e7b1SMilanka Ringwald     if (client == NULL){
1574cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1575cd28e7b1SMilanka Ringwald     }
1576cd28e7b1SMilanka Ringwald 
1577cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1578cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1579cd28e7b1SMilanka Ringwald     }
1580cd28e7b1SMilanka Ringwald 
1581cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1582cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1583cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1584cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1585cd28e7b1SMilanka Ringwald     }
1586cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1587cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1588cd28e7b1SMilanka Ringwald }
15896d6f7efcSMilanka Ringwald 
1590021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1591021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1592021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1593021192e1SMilanka Ringwald }
1594cf26c8fbSMilanka Ringwald 
1595cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1596