xref: /btstack/src/ble/gatt-service/hids_client.c (revision cd28e7b1030e8633477fdfda374ac258eab1e4b3)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23fc975d0eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24fc975d0eSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/att_db.h"
53fc975d0eSMilanka Ringwald #include "ble/core.h"
54fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
55fc975d0eSMilanka Ringwald #include "ble/sm.h"
56cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
57fc975d0eSMilanka Ringwald #include "btstack_debug.h"
58fc975d0eSMilanka Ringwald #include "btstack_event.h"
59fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
60fc975d0eSMilanka Ringwald #include "gap.h"
61fc975d0eSMilanka Ringwald 
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6570093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6670093cf5SMilanka Ringwald 
67da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
68da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
69da142a6fSMilanka Ringwald 
70da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
71da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
72da142a6fSMilanka Ringwald 
73cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
74cf26c8fbSMilanka Ringwald 
75556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
76556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
77556456ccSMilanka Ringwald     switch (uuid){
78556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
79556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
80556456ccSMilanka Ringwald 
81556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
82556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
83556456ccSMilanka Ringwald 
84556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
85556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
86556456ccSMilanka Ringwald 
87556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
88556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
89556456ccSMilanka Ringwald 
90556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
91556456ccSMilanka Ringwald             return "REPORT";
92556456ccSMilanka Ringwald 
93556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94556456ccSMilanka Ringwald             return "REPORT_MAP";
95556456ccSMilanka Ringwald 
96556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
97556456ccSMilanka Ringwald             return "HID_INFORMATION";
98556456ccSMilanka Ringwald 
99556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
100556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
101556456ccSMilanka Ringwald         default:
102556456ccSMilanka Ringwald             return "UKNOWN";
103556456ccSMilanka Ringwald     }
104556456ccSMilanka Ringwald }
105556456ccSMilanka Ringwald #endif
106556456ccSMilanka Ringwald 
107da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
109da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
110da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
111da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
112da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
113da142a6fSMilanka Ringwald         return client;
114da142a6fSMilanka Ringwald     }
115da142a6fSMilanka Ringwald     return NULL;
116da142a6fSMilanka Ringwald }
117da142a6fSMilanka Ringwald 
118da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
120da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
121da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
122da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
123da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
124da142a6fSMilanka Ringwald         return client;
125da142a6fSMilanka Ringwald     }
126da142a6fSMilanka Ringwald     return NULL;
127da142a6fSMilanka Ringwald }
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald // START Descriptor Storage Util
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
133da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
134da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
135da142a6fSMilanka Ringwald     uint8_t i;
136da142a6fSMilanka Ringwald 
137da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
138da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
139da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
140da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
141da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
142da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
143da142a6fSMilanka Ringwald         }
144da142a6fSMilanka Ringwald     }
145da142a6fSMilanka Ringwald     return free_space;
146da142a6fSMilanka Ringwald }
147da142a6fSMilanka Ringwald 
148da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
150da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
151da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
155da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
156da142a6fSMilanka Ringwald 
157da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
159da142a6fSMilanka Ringwald     return true;
160da142a6fSMilanka Ringwald }
161da142a6fSMilanka Ringwald 
162da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
163da142a6fSMilanka Ringwald     uint8_t service_index = 0;
164da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
165da142a6fSMilanka Ringwald 
166da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
167da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
168da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
169da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
170da142a6fSMilanka Ringwald     }
171da142a6fSMilanka Ringwald 
172da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
173da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
174da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
175da142a6fSMilanka Ringwald 
176da142a6fSMilanka Ringwald     uint8_t i;
177da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
178da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
179da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
180da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
181da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
182da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
183da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
184da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
185da142a6fSMilanka Ringwald             }
186da142a6fSMilanka Ringwald         }
187da142a6fSMilanka Ringwald     }
188da142a6fSMilanka Ringwald }
189da142a6fSMilanka Ringwald 
190da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
191da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
192021192e1SMilanka Ringwald     if (client == NULL){
193021192e1SMilanka Ringwald         return NULL;
194021192e1SMilanka Ringwald     }
195021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
196da142a6fSMilanka Ringwald         return NULL;
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
199da142a6fSMilanka Ringwald }
200da142a6fSMilanka Ringwald 
201da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
202da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
203021192e1SMilanka Ringwald     if (client == NULL){
204021192e1SMilanka Ringwald         return 0;
205021192e1SMilanka Ringwald     }
206021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
207da142a6fSMilanka Ringwald         return 0;
208da142a6fSMilanka Ringwald     }
209da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
210da142a6fSMilanka Ringwald }
211da142a6fSMilanka Ringwald 
212da142a6fSMilanka Ringwald // END Descriptor Storage Util
213da142a6fSMilanka Ringwald 
214cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
215cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
216cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
217cf26c8fbSMilanka Ringwald     } else {
218cf26c8fbSMilanka Ringwald         hids_cid_counter++;
219cf26c8fbSMilanka Ringwald     }
220cf26c8fbSMilanka Ringwald     return hids_cid_counter;
221fc975d0eSMilanka Ringwald }
222fc975d0eSMilanka Ringwald 
223ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
224ab116b1cSMilanka Ringwald     uint8_t i;
225a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
226ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
227ab116b1cSMilanka Ringwald             return i;
228ab116b1cSMilanka Ringwald         }
229ab116b1cSMilanka Ringwald     }
230ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
231ab116b1cSMilanka Ringwald }
232ab116b1cSMilanka Ringwald 
23319146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
23419146789SMilanka Ringwald     uint8_t i;
23519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
23619146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
23719146789SMilanka Ringwald             return i;
23819146789SMilanka Ringwald         }
23919146789SMilanka Ringwald     }
24019146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
24119146789SMilanka Ringwald }
24219146789SMilanka Ringwald 
24319146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
24419146789SMilanka Ringwald     uint8_t i;
24519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24619146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
24719146789SMilanka Ringwald             return true;
24819146789SMilanka Ringwald         }
24919146789SMilanka Ringwald     }
25019146789SMilanka Ringwald     return false;
25119146789SMilanka Ringwald }
25219146789SMilanka Ringwald 
253835a13f1SMilanka Ringwald static uint8_t find_report_index_for_report_id(hids_client_t * client, uint8_t report_id){
25483d7ed1cSMilanka Ringwald     uint8_t i;
255835a13f1SMilanka Ringwald 
25683d7ed1cSMilanka Ringwald     switch (client->protocol_mode){
25783d7ed1cSMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
25883d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
25983d7ed1cSMilanka Ringwald                 if (!client->reports[i].boot_report){
26083d7ed1cSMilanka Ringwald                     continue;
26183d7ed1cSMilanka Ringwald                 }
262835a13f1SMilanka Ringwald                 if (client->reports[i].report_id == report_id){
26383d7ed1cSMilanka Ringwald                     return i;
26483d7ed1cSMilanka Ringwald                 }
26583d7ed1cSMilanka Ringwald             }
26683d7ed1cSMilanka Ringwald             break;
26783d7ed1cSMilanka Ringwald 
26883d7ed1cSMilanka Ringwald         default:
26983d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
27083d7ed1cSMilanka Ringwald                 if (client->reports[i].boot_report){
27183d7ed1cSMilanka Ringwald                     continue;
27283d7ed1cSMilanka Ringwald                 }
273835a13f1SMilanka Ringwald                 if (client->reports[i].report_id == report_id){
27483d7ed1cSMilanka Ringwald                     return i;
27583d7ed1cSMilanka Ringwald                 }
27683d7ed1cSMilanka Ringwald             }
27783d7ed1cSMilanka Ringwald             break;
27883d7ed1cSMilanka Ringwald     }
27983d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
28083d7ed1cSMilanka Ringwald }
28119146789SMilanka Ringwald 
282021192e1SMilanka Ringwald static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type, bool boot_report){
283ab116b1cSMilanka Ringwald 
28419146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
285ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2863322b222SMilanka Ringwald         return report_index;
287ab116b1cSMilanka Ringwald     }
28828da36a6SMilanka Ringwald     report_index = client->num_reports;
289ab116b1cSMilanka Ringwald 
29028da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
29128da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
29228da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
29328da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
294ab116b1cSMilanka Ringwald 
29528da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
29628da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
29728da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
298021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
29970093cf5SMilanka Ringwald 
300021192e1SMilanka Ringwald         log_info("add index %d, id %d, type %d, value handle 0x%02x, properties 0x%02x", report_index, report_id, report_type, characteristic->value_handle, characteristic->properties);
301ab116b1cSMilanka Ringwald         client->num_reports++;
3023322b222SMilanka Ringwald         return report_index;
303ab116b1cSMilanka Ringwald     } else {
304ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3053322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
306ab116b1cSMilanka Ringwald     }
307ab116b1cSMilanka Ringwald }
308ab116b1cSMilanka Ringwald 
30919146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
310556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
311556456ccSMilanka Ringwald 
312556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
31319146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
314556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
31519146789SMilanka Ringwald 
316556456ccSMilanka Ringwald         client->num_external_reports++;
31719146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
318556456ccSMilanka Ringwald         return report_index;
319556456ccSMilanka Ringwald     } else {
320556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
321556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
32270093cf5SMilanka Ringwald     }
323556456ccSMilanka Ringwald }
324556456ccSMilanka Ringwald 
32570093cf5SMilanka Ringwald 
326021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
327021192e1SMilanka Ringwald     uint8_t i;
328021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
329021192e1SMilanka Ringwald         if (!client->reports[i].boot_report){
330021192e1SMilanka Ringwald             return true;
331ab116b1cSMilanka Ringwald         }
332021192e1SMilanka Ringwald     }
333021192e1SMilanka Ringwald     return false;
334021192e1SMilanka Ringwald }
335021192e1SMilanka Ringwald 
336021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
337021192e1SMilanka Ringwald     uint8_t i;
338021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
339021192e1SMilanka Ringwald         if (client->reports[i].boot_report){
340021192e1SMilanka Ringwald             return true;
341021192e1SMilanka Ringwald         }
342021192e1SMilanka Ringwald     }
343021192e1SMilanka Ringwald     return false;
344ab116b1cSMilanka Ringwald }
345ab116b1cSMilanka Ringwald 
3467e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3477e1e6e7dSMilanka Ringwald     uint8_t i;
3483322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3493322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
350556456ccSMilanka Ringwald             return i;
3513322b222SMilanka Ringwald         }
3523322b222SMilanka Ringwald     }
353556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
354556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3553322b222SMilanka Ringwald }
3563322b222SMilanka Ringwald 
3573322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3583322b222SMilanka Ringwald     client->service_index++;
3593322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
360556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3613322b222SMilanka Ringwald         return true;
3623322b222SMilanka Ringwald     }
3633322b222SMilanka Ringwald     return false;
3643322b222SMilanka Ringwald }
3653322b222SMilanka Ringwald 
3663322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3673322b222SMilanka Ringwald     client->service_index = 0;
3683322b222SMilanka Ringwald 
3693322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3703322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3713322b222SMilanka Ringwald         return true;
3723322b222SMilanka Ringwald     }
3733322b222SMilanka Ringwald     return false;
3743322b222SMilanka Ringwald }
3753322b222SMilanka Ringwald 
3763322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
377021192e1SMilanka Ringwald     client->report_index++;
37819146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3793322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
380e7bd2dbeSMilanka Ringwald         return true;
381e7bd2dbeSMilanka Ringwald     }
382e7bd2dbeSMilanka Ringwald     return false;
383e7bd2dbeSMilanka Ringwald }
384e7bd2dbeSMilanka Ringwald 
3853322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
386021192e1SMilanka Ringwald     client->report_index = 0;
38719146789SMilanka Ringwald     if (client->num_external_reports > 0){
3883322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3897e1e6e7dSMilanka Ringwald         return true;
3907e1e6e7dSMilanka Ringwald     }
3917e1e6e7dSMilanka Ringwald     return false;
3927e1e6e7dSMilanka Ringwald }
3937e1e6e7dSMilanka Ringwald 
3947e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3957e1e6e7dSMilanka Ringwald     uint8_t i;
3967e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
397021192e1SMilanka Ringwald     switch (client->protocol_mode){
398021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
399021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
40070093cf5SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
40170093cf5SMilanka Ringwald                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
4027e1e6e7dSMilanka Ringwald                     index = i;
403021192e1SMilanka Ringwald                     client->service_index = report.service_index;
404021192e1SMilanka Ringwald                 }
405021192e1SMilanka Ringwald             }
406021192e1SMilanka Ringwald             break;
407021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
408021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
409021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
410021192e1SMilanka Ringwald                 if (report.boot_report){
411021192e1SMilanka Ringwald                     index = i;
412021192e1SMilanka Ringwald                     client->service_index = report.service_index;
413021192e1SMilanka Ringwald                 }
414021192e1SMilanka Ringwald             }
415021192e1SMilanka Ringwald             break;
416021192e1SMilanka Ringwald         default:
4177e1e6e7dSMilanka Ringwald             break;
41870093cf5SMilanka Ringwald     }
419021192e1SMilanka Ringwald 
420021192e1SMilanka Ringwald     client->report_index = index;
4217e1e6e7dSMilanka Ringwald     return index;
4227e1e6e7dSMilanka Ringwald }
4237e1e6e7dSMilanka Ringwald 
4247e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
425021192e1SMilanka Ringwald     client->report_index++;
4267e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
42728da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
428e7bd2dbeSMilanka Ringwald         return true;
42970093cf5SMilanka Ringwald     }
4307e1e6e7dSMilanka Ringwald     return false;
4317e1e6e7dSMilanka Ringwald }
4327e1e6e7dSMilanka Ringwald 
4337e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
434021192e1SMilanka Ringwald     client->report_index = 0;
4357e1e6e7dSMilanka Ringwald 
4367e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
43728da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4387e1e6e7dSMilanka Ringwald         return true;
4397e1e6e7dSMilanka Ringwald     }
440e7bd2dbeSMilanka Ringwald     return false;
44170093cf5SMilanka Ringwald }
442ab116b1cSMilanka Ringwald 
443021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
444021192e1SMilanka Ringwald     uint8_t i;
445021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
446021192e1SMilanka Ringwald 
447021192e1SMilanka Ringwald     switch (client->protocol_mode){
448021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
449021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
450021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
451021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
452021192e1SMilanka Ringwald                     continue;
453021192e1SMilanka Ringwald                 }
454021192e1SMilanka Ringwald                 if (!report.boot_report){
455021192e1SMilanka Ringwald                     index = i;
456021192e1SMilanka Ringwald                 }
457021192e1SMilanka Ringwald             }
458021192e1SMilanka Ringwald             break;
459021192e1SMilanka Ringwald 
460021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
461021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
462021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
463021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
464021192e1SMilanka Ringwald                     continue;
465021192e1SMilanka Ringwald                 }
466021192e1SMilanka Ringwald                 if (report.boot_report){
467021192e1SMilanka Ringwald                     index = i;
468021192e1SMilanka Ringwald                 }
469021192e1SMilanka Ringwald             }
470021192e1SMilanka Ringwald             break;
471021192e1SMilanka Ringwald 
472021192e1SMilanka Ringwald         default:
473021192e1SMilanka Ringwald             break;
474021192e1SMilanka Ringwald     }
475021192e1SMilanka Ringwald 
476021192e1SMilanka Ringwald     client->report_index = index;
477021192e1SMilanka Ringwald     return index;
478021192e1SMilanka Ringwald }
479021192e1SMilanka Ringwald 
480021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
481021192e1SMilanka Ringwald     client->report_index++;
482021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
483021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
484021192e1SMilanka Ringwald         return true;
485021192e1SMilanka Ringwald     }
486021192e1SMilanka Ringwald     return false;
487021192e1SMilanka Ringwald }
488021192e1SMilanka Ringwald 
489021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
490*cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
491*cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
492*cd28e7b1SMilanka Ringwald #endif
493021192e1SMilanka Ringwald     client->report_index = 0;
494021192e1SMilanka Ringwald 
495021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
496021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
497021192e1SMilanka Ringwald         return true;
498021192e1SMilanka Ringwald     }
499021192e1SMilanka Ringwald     return false;
500021192e1SMilanka Ringwald }
501021192e1SMilanka Ringwald 
502*cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
503*cd28e7b1SMilanka Ringwald     client->report_index++;
504*cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
505*cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
506*cd28e7b1SMilanka Ringwald         return true;
507*cd28e7b1SMilanka Ringwald     }
508*cd28e7b1SMilanka Ringwald     return false;
509*cd28e7b1SMilanka Ringwald }
510*cd28e7b1SMilanka Ringwald 
511*cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
512*cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
513*cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
514*cd28e7b1SMilanka Ringwald #endif
515*cd28e7b1SMilanka Ringwald     client->report_index = 0;
516*cd28e7b1SMilanka Ringwald 
517*cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
518*cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
519*cd28e7b1SMilanka Ringwald         return true;
520*cd28e7b1SMilanka Ringwald     }
521*cd28e7b1SMilanka Ringwald     return false;
522*cd28e7b1SMilanka Ringwald }
523*cd28e7b1SMilanka Ringwald 
524cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
525cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
526cf26c8fbSMilanka Ringwald     if (!client){
527cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
528cf26c8fbSMilanka Ringwald         return NULL;
529cf26c8fbSMilanka Ringwald     }
530cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
531cf26c8fbSMilanka Ringwald     client->cid = cid;
532cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
533fc975d0eSMilanka Ringwald 
534cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
535cf26c8fbSMilanka Ringwald     return client;
536fc975d0eSMilanka Ringwald }
537fc975d0eSMilanka Ringwald 
538cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
539021192e1SMilanka Ringwald     // stop listening
540021192e1SMilanka Ringwald     uint8_t i;
541021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
542021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
543021192e1SMilanka Ringwald     }
544021192e1SMilanka Ringwald 
545da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
546cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
547cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
548fc975d0eSMilanka Ringwald }
549cf26c8fbSMilanka Ringwald 
550cf26c8fbSMilanka Ringwald 
551cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5526bcfb631SMilanka Ringwald     uint8_t event[8];
553cf26c8fbSMilanka Ringwald     int pos = 0;
554cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
555cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
556cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
557cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
558cf26c8fbSMilanka Ringwald     pos += 2;
559cf26c8fbSMilanka Ringwald     event[pos++] = status;
5606bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
561cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
562cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
563cf26c8fbSMilanka Ringwald }
564cf26c8fbSMilanka Ringwald 
565*cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
566*cd28e7b1SMilanka Ringwald     uint8_t event[6];
567*cd28e7b1SMilanka Ringwald     int pos = 0;
568*cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
569*cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
570*cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
571*cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
572*cd28e7b1SMilanka Ringwald     pos += 2;
573*cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
574*cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
575*cd28e7b1SMilanka Ringwald }
576*cd28e7b1SMilanka Ringwald 
577021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
578021192e1SMilanka Ringwald     uint16_t pos = 0;
579021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
580021192e1SMilanka Ringwald     pos++;  // skip len
581021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
582021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
583021192e1SMilanka Ringwald     pos += 2;
584021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
585021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
586021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
587021192e1SMilanka Ringwald     pos += 2;
588021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
58983d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
59083d7ed1cSMilanka Ringwald 
591021192e1SMilanka Ringwald }
592021192e1SMilanka Ringwald 
593f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
594f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
595f4d3b82aSMilanka Ringwald 
596f4d3b82aSMilanka Ringwald     uint8_t event[11];
597f4d3b82aSMilanka Ringwald     int pos = 0;
598f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
599f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
600f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
601f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
602f4d3b82aSMilanka Ringwald     pos += 2;
603f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
604f4d3b82aSMilanka Ringwald 
605f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
606f4d3b82aSMilanka Ringwald     pos += 3;
607f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
608f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
609f4d3b82aSMilanka Ringwald 
610f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
611f4d3b82aSMilanka Ringwald }
612f4d3b82aSMilanka Ringwald 
613af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
614af2241c2SMilanka Ringwald     if (value_len != 1) return;
615af2241c2SMilanka Ringwald 
616af2241c2SMilanka Ringwald     uint8_t event[11];
617af2241c2SMilanka Ringwald     int pos = 0;
618af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
619af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
620af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
621af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
622af2241c2SMilanka Ringwald     pos += 2;
623af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
624af2241c2SMilanka Ringwald     event[pos++] = value[0];
625af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
626af2241c2SMilanka Ringwald }
627f4d3b82aSMilanka Ringwald 
62883d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
629021192e1SMilanka Ringwald     UNUSED(packet_type);
630021192e1SMilanka Ringwald     UNUSED(channel);
63183d7ed1cSMilanka Ringwald 
632021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
633021192e1SMilanka Ringwald 
634021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
635021192e1SMilanka Ringwald     btstack_assert(client != NULL);
636021192e1SMilanka Ringwald 
637021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
638021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
639021192e1SMilanka Ringwald         return;
640021192e1SMilanka Ringwald     }
641021192e1SMilanka Ringwald 
64283d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
643021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
64483d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
645021192e1SMilanka Ringwald }
646021192e1SMilanka Ringwald 
64783d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
64883d7ed1cSMilanka Ringwald     UNUSED(packet_type);
64983d7ed1cSMilanka Ringwald     UNUSED(channel);
65083d7ed1cSMilanka Ringwald 
65183d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
65283d7ed1cSMilanka Ringwald 
65383d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
65483d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
65583d7ed1cSMilanka Ringwald 
65683d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
65783d7ed1cSMilanka Ringwald         return;
65883d7ed1cSMilanka Ringwald     }
65983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
66083d7ed1cSMilanka Ringwald 
66183d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
66283d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
66383d7ed1cSMilanka Ringwald         return;
66483d7ed1cSMilanka Ringwald     }
66583d7ed1cSMilanka Ringwald 
66683d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
66783d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
66883d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
66983d7ed1cSMilanka Ringwald }
670cf26c8fbSMilanka Ringwald 
671cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
672cf26c8fbSMilanka Ringwald     uint8_t att_status;
6736bcfb631SMilanka Ringwald     gatt_client_service_t service;
6746bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
675cf26c8fbSMilanka Ringwald 
676cf26c8fbSMilanka Ringwald     switch (client->state){
677cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
678556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
679556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
680556456ccSMilanka Ringwald #endif
681cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
68219146789SMilanka Ringwald 
68319146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
684cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
685cf26c8fbSMilanka Ringwald             UNUSED(att_status);
686cf26c8fbSMilanka Ringwald             break;
687cf26c8fbSMilanka Ringwald 
688cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
689556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
690556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
691556456ccSMilanka Ringwald #endif
6926bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6936bcfb631SMilanka Ringwald 
6946bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6956bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
69619146789SMilanka Ringwald 
69719146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6986bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6996bcfb631SMilanka Ringwald 
7006bcfb631SMilanka Ringwald             UNUSED(att_status);
7016bcfb631SMilanka Ringwald             break;
7026bcfb631SMilanka Ringwald 
703021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
7042901a9b7SMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
7056bcfb631SMilanka Ringwald             UNUSED(att_status);
7062901a9b7SMilanka Ringwald 
7072901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
708021192e1SMilanka Ringwald             if (hids_client_report_query_init(client)){
709021192e1SMilanka Ringwald                 break;
710021192e1SMilanka Ringwald             }
711021192e1SMilanka Ringwald 
712021192e1SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
713021192e1SMilanka Ringwald             hids_finalize_client(client);
714cf26c8fbSMilanka Ringwald             break;
715cf26c8fbSMilanka Ringwald 
71628da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
717556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
718556456ccSMilanka 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);
719556456ccSMilanka Ringwald #endif
72028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
721556456ccSMilanka Ringwald 
72219146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
72319146789SMilanka 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);
724e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
725e7bd2dbeSMilanka Ringwald             break;
726e7bd2dbeSMilanka Ringwald 
727e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
728556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
729556456ccSMilanka 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);
730556456ccSMilanka Ringwald #endif
731e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
732e7bd2dbeSMilanka Ringwald 
7333322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
7343322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
7353322b222SMilanka Ringwald 
73619146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
737e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
738e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
739e7bd2dbeSMilanka Ringwald             break;
740e7bd2dbeSMilanka Ringwald 
74128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
742556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
74319146789SMilanka 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);
744556456ccSMilanka Ringwald #endif
74528da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
746e7bd2dbeSMilanka Ringwald 
74719146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
74819146789SMilanka 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);
749e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
750e7bd2dbeSMilanka Ringwald             break;
751e7bd2dbeSMilanka Ringwald 
7523322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
753556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
75419146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
755556456ccSMilanka Ringwald #endif
7563322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
757e7bd2dbeSMilanka Ringwald 
7583322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7593322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
76019146789SMilanka Ringwald 
76119146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7623322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
763e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
764e7bd2dbeSMilanka Ringwald             break;
765e7bd2dbeSMilanka Ringwald 
76628da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
767556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
76883d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
769556456ccSMilanka Ringwald                 client->report_index,
77083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
77183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
772556456ccSMilanka Ringwald #endif
77328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
774af2241c2SMilanka Ringwald             client->handle = 0;
775556456ccSMilanka Ringwald 
776556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
777556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
778556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
779556456ccSMilanka Ringwald 
78019146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
78170093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
78270093cf5SMilanka Ringwald             UNUSED(att_status);
78370093cf5SMilanka Ringwald             break;
78470093cf5SMilanka Ringwald 
78528da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
78628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
78770093cf5SMilanka Ringwald 
78819146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
789af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
790af2241c2SMilanka Ringwald             client->handle = 0;
79170093cf5SMilanka Ringwald             UNUSED(att_status);
79270093cf5SMilanka Ringwald             break;
79370093cf5SMilanka Ringwald 
794*cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
795*cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
796*cd28e7b1SMilanka Ringwald             if (client->value > 0){
797*cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
798*cd28e7b1SMilanka Ringwald             } else {
799*cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
800*cd28e7b1SMilanka Ringwald             }
801*cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
802*cd28e7b1SMilanka Ringwald                 client->report_index,
803*cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
804*cd28e7b1SMilanka Ringwald #endif
805*cd28e7b1SMilanka Ringwald 
806*cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
807*cd28e7b1SMilanka Ringwald 
808*cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
809*cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
810*cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
811*cd28e7b1SMilanka Ringwald 
812*cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
813*cd28e7b1SMilanka Ringwald 
814*cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
815*cd28e7b1SMilanka Ringwald 
816*cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
817*cd28e7b1SMilanka Ringwald                 switch(client->value){
818*cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
819*cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
820*cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
821*cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
822*cd28e7b1SMilanka Ringwald                         break;
823*cd28e7b1SMilanka Ringwald                     default:
824*cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
825*cd28e7b1SMilanka Ringwald                         break;
826*cd28e7b1SMilanka Ringwald                 }
827*cd28e7b1SMilanka Ringwald             } else {
828*cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
829*cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
830*cd28e7b1SMilanka Ringwald                     break;
831*cd28e7b1SMilanka Ringwald                 }
832*cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
833*cd28e7b1SMilanka Ringwald             }
834*cd28e7b1SMilanka Ringwald             break;
835*cd28e7b1SMilanka Ringwald 
836021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
837*cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
838*cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
839*cd28e7b1SMilanka Ringwald                 client->report_index,
840*cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
841*cd28e7b1SMilanka Ringwald #endif
842021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
843021192e1SMilanka Ringwald 
844021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
845021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
846021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
847021192e1SMilanka Ringwald 
84819146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
849021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
850021192e1SMilanka Ringwald 
851*cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
852*cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
853*cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
854*cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
855*cd28e7b1SMilanka Ringwald             } else {
856021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
857021192e1SMilanka Ringwald                     hids_run_for_client(client);
858021192e1SMilanka Ringwald                     break;
859021192e1SMilanka Ringwald                 }
860021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
861021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
862021192e1SMilanka Ringwald             }
863021192e1SMilanka Ringwald             break;
864021192e1SMilanka Ringwald 
86583d7ed1cSMilanka Ringwald 
8666d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
86783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8686d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
86983d7ed1cSMilanka Ringwald                 client->report_index,
87083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
87183d7ed1cSMilanka Ringwald #endif
87283d7ed1cSMilanka Ringwald 
8736d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
87483d7ed1cSMilanka Ringwald 
8756d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8766d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
8776d6f7efcSMilanka Ringwald                 &handle_report_event, client->con_handle,
87883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
87983d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
88083d7ed1cSMilanka Ringwald             UNUSED(att_status);
88183d7ed1cSMilanka Ringwald             break;
88283d7ed1cSMilanka Ringwald 
88383d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
88483d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
885f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
886f4d3b82aSMilanka Ringwald                 client->report_index,
88783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
88883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
88983d7ed1cSMilanka Ringwald #endif
89083d7ed1cSMilanka Ringwald 
89183d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
892f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
89383d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
89483d7ed1cSMilanka Ringwald                 &handle_report_event,
89583d7ed1cSMilanka Ringwald                 client->con_handle,
89683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
89783d7ed1cSMilanka Ringwald             UNUSED(att_status);
89883d7ed1cSMilanka Ringwald             break;
89983d7ed1cSMilanka Ringwald 
90083d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
90183d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
90283d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
90383d7ed1cSMilanka Ringwald 
904f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
90583d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
90683d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
90783d7ed1cSMilanka Ringwald                 client->con_handle,
90883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
90983d7ed1cSMilanka Ringwald 
91083d7ed1cSMilanka Ringwald             break;
91183d7ed1cSMilanka Ringwald #endif
912af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
913af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
91483d7ed1cSMilanka Ringwald 
915f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
916f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
917f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
918f4d3b82aSMilanka Ringwald                 client->con_handle,
919af2241c2SMilanka Ringwald                 client->handle);
920f4d3b82aSMilanka Ringwald             break;
9216d6f7efcSMilanka Ringwald 
9226d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9236d6f7efcSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9246d6f7efcSMilanka Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9256d6f7efcSMilanka Ringwald #endif
9266d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9276d6f7efcSMilanka Ringwald 
9286d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9296d6f7efcSMilanka Ringwald             UNUSED(att_status);
9306d6f7efcSMilanka Ringwald             break;
9316d6f7efcSMilanka Ringwald 
9326bcfb631SMilanka Ringwald         default:
9336bcfb631SMilanka Ringwald             break;
9346bcfb631SMilanka Ringwald     }
9356bcfb631SMilanka Ringwald }
9366bcfb631SMilanka Ringwald 
937cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
938cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
939cf26c8fbSMilanka Ringwald     UNUSED(channel);
940cf26c8fbSMilanka Ringwald     UNUSED(size);
941cf26c8fbSMilanka Ringwald 
9426bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
943cf26c8fbSMilanka Ringwald     uint8_t att_status;
9446bcfb631SMilanka Ringwald     gatt_client_service_t service;
9456bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
94670093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
947ab116b1cSMilanka Ringwald 
948021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
949021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
950e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9513322b222SMilanka Ringwald     uint8_t i;
9523322b222SMilanka Ringwald     uint8_t report_index;
953da142a6fSMilanka Ringwald 
954f4d3b82aSMilanka Ringwald     const uint8_t * value;
955f4d3b82aSMilanka Ringwald     uint16_t value_len;
956cf26c8fbSMilanka Ringwald 
957cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
958cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
959cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
960cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
961cf26c8fbSMilanka Ringwald 
9626bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
9636bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
9646bcfb631SMilanka Ringwald                 hids_finalize_client(client);
9656bcfb631SMilanka Ringwald                 break;
9666bcfb631SMilanka Ringwald             }
9676bcfb631SMilanka Ringwald 
968021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
969021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9706bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
971021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
972021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9736bcfb631SMilanka Ringwald                 client->num_instances++;
974021192e1SMilanka Ringwald 
975708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
976708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
977708c69d2SMilanka Ringwald #endif
978021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
979021192e1SMilanka Ringwald             }  else {
980021192e1SMilanka 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);
981da142a6fSMilanka Ringwald             }
9826bcfb631SMilanka Ringwald             break;
9836bcfb631SMilanka Ringwald 
9846bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9856bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
9866bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
9876bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
98819146789SMilanka Ringwald 
98919146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
99019146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
99119146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
99219146789SMilanka Ringwald                     break;
99319146789SMilanka Ringwald                 }
99419146789SMilanka Ringwald             }
99519146789SMilanka Ringwald 
99619146789SMilanka Ringwald             switch (characteristic.uuid16){
99719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
99819146789SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
999af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
100019146789SMilanka Ringwald                     break;
100119146789SMilanka Ringwald 
100219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
100319146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
100419146789SMilanka Ringwald                     break;
100519146789SMilanka Ringwald 
100619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
100719146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
100819146789SMilanka Ringwald                     break;
100919146789SMilanka Ringwald 
101019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1011f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
101219146789SMilanka Ringwald                     break;
101319146789SMilanka Ringwald 
101419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
101519146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
101619146789SMilanka Ringwald                     break;
101719146789SMilanka Ringwald 
101819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
101919146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
102019146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
102119146789SMilanka Ringwald                     break;
102219146789SMilanka Ringwald 
102319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1024f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
102519146789SMilanka Ringwald                     break;
102619146789SMilanka Ringwald 
102719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1028f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
102919146789SMilanka Ringwald                     break;
103019146789SMilanka Ringwald 
103119146789SMilanka Ringwald                 default:
103219146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103319146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
103419146789SMilanka Ringwald #endif
103519146789SMilanka Ringwald                     return;
103619146789SMilanka Ringwald             }
103719146789SMilanka Ringwald 
103819146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103919146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
104019146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
104119146789SMilanka Ringwald                 characteristic.start_handle,
104219146789SMilanka Ringwald                 characteristic.properties,
104319146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
104419146789SMilanka Ringwald                 client->service_index);
104519146789SMilanka Ringwald 
104619146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
104719146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
104819146789SMilanka Ringwald             }
104919146789SMilanka Ringwald             printf("\n");
105019146789SMilanka Ringwald #endif
1051cf26c8fbSMilanka Ringwald             break;
1052cf26c8fbSMilanka Ringwald 
10538cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1054e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10558cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
1056e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
1057da142a6fSMilanka Ringwald 
1058f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1059f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10608cec2b74SMilanka Ringwald 
1061556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10628cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1063f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1064556456ccSMilanka Ringwald #endif
1065f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1066f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1067da142a6fSMilanka Ringwald                 if (!stored){
1068da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1069da142a6fSMilanka Ringwald                     break;
1070da142a6fSMilanka Ringwald                 }
1071da142a6fSMilanka Ringwald             }
1072e7bd2dbeSMilanka Ringwald             break;
1073e7bd2dbeSMilanka Ringwald 
107470093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
107570093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
107670093cf5SMilanka Ringwald             btstack_assert(client != NULL);
107770093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
107870093cf5SMilanka Ringwald 
1079e7bd2dbeSMilanka Ringwald             switch (client->state) {
1080e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1081e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1082e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
108319146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10843322b222SMilanka Ringwald 
1085556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1086556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1087556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1088556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1089556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1090556456ccSMilanka Ringwald                                 client->service_index, report_index);
1091556456ccSMilanka Ringwald                         }
1092556456ccSMilanka Ringwald #endif
1093e7bd2dbeSMilanka Ringwald                     }
1094e7bd2dbeSMilanka Ringwald                     break;
109528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
109670093cf5SMilanka Ringwald                     // setup for descriptor value query
109770093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1098af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1099556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1100556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1101556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1102556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
110319146789SMilanka Ringwald #endif
1104556456ccSMilanka Ringwald                     }
1105556456ccSMilanka Ringwald 
110619146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1107556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
110883d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1109556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1110556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1111556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1112556456ccSMilanka Ringwald                     }
1113556456ccSMilanka Ringwald #endif
111470093cf5SMilanka Ringwald                     break;
1115556456ccSMilanka Ringwald 
1116e7bd2dbeSMilanka Ringwald                 default:
1117e7bd2dbeSMilanka Ringwald                     break;
1118e7bd2dbeSMilanka Ringwald             }
1119e7bd2dbeSMilanka Ringwald             break;
112070093cf5SMilanka Ringwald 
112183d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
112283d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
112383d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
112483d7ed1cSMilanka Ringwald 
1125f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1126f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1127f4d3b82aSMilanka Ringwald 
1128af2241c2SMilanka Ringwald 
1129f4d3b82aSMilanka Ringwald             switch (client->state){
1130f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1131f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
113283d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1133f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
113483d7ed1cSMilanka Ringwald                     break;
113583d7ed1cSMilanka Ringwald #endif
1136af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1137af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1138af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1139f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1140f4d3b82aSMilanka Ringwald                         break;
1141af2241c2SMilanka Ringwald                     }
1142af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1143af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1144af2241c2SMilanka Ringwald                         break;
1145af2241c2SMilanka Ringwald                     }
1146af2241c2SMilanka Ringwald                     break;
1147af2241c2SMilanka Ringwald                 }
1148f4d3b82aSMilanka Ringwald                 default:
1149f4d3b82aSMilanka Ringwald                     break;
1150f4d3b82aSMilanka Ringwald             }
1151f4d3b82aSMilanka Ringwald 
1152f4d3b82aSMilanka Ringwald             break;
115383d7ed1cSMilanka Ringwald 
115470093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
115570093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
115670093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1157e7bd2dbeSMilanka Ringwald 
1158e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1159e7bd2dbeSMilanka Ringwald                 break;
1160e7bd2dbeSMilanka Ringwald             }
11617e1e6e7dSMilanka Ringwald 
1162e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1163e7bd2dbeSMilanka Ringwald             switch (client->state) {
116428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1165e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
116619146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11673322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
116819146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1169556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
117019146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
117119146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1172556456ccSMilanka Ringwald #endif
11733322b222SMilanka Ringwald                     }
1174e7bd2dbeSMilanka Ringwald                     break;
1175e7bd2dbeSMilanka Ringwald 
117628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
117719146789SMilanka Ringwald 
117819146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1179021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1180021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
118119146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
118283d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1183021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1184021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
118583d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
118619146789SMilanka Ringwald     #endif
118719146789SMilanka Ringwald                     }
1188e7bd2dbeSMilanka Ringwald                     break;
1189e7bd2dbeSMilanka Ringwald 
1190e7bd2dbeSMilanka Ringwald                 default:
1191e7bd2dbeSMilanka Ringwald                     break;
119270093cf5SMilanka Ringwald             }
119370093cf5SMilanka Ringwald             break;
119470093cf5SMilanka Ringwald 
1195cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1196cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1197cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1198cf26c8fbSMilanka Ringwald 
1199cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1200cf26c8fbSMilanka Ringwald 
1201cf26c8fbSMilanka Ringwald             switch (client->state){
1202cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
12036bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12046bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12056bcfb631SMilanka Ringwald                         hids_finalize_client(client);
1206cf26c8fbSMilanka Ringwald                         break;
12076bcfb631SMilanka Ringwald                     }
12086bcfb631SMilanka Ringwald 
12096bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12106bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12116bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12126bcfb631SMilanka Ringwald                         break;
12136bcfb631SMilanka Ringwald                     }
12146bcfb631SMilanka Ringwald 
12156bcfb631SMilanka Ringwald                     client->service_index = 0;
121619146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12176bcfb631SMilanka Ringwald                     break;
12186bcfb631SMilanka Ringwald 
12196bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
12206bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12216bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12226bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12236bcfb631SMilanka Ringwald                         break;
12246bcfb631SMilanka Ringwald                     }
12256bcfb631SMilanka Ringwald 
122670093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
122770093cf5SMilanka Ringwald                         // discover characteristics of next service
122870093cf5SMilanka Ringwald                         client->service_index++;
122970093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1230021192e1SMilanka Ringwald                         break;
1231021192e1SMilanka Ringwald                     }
1232021192e1SMilanka Ringwald 
1233021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1234021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1235021192e1SMilanka Ringwald                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1236021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1237021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1238021192e1SMilanka Ringwald                                 break;
1239021192e1SMilanka Ringwald                             }
1240021192e1SMilanka Ringwald                             hids_emit_connection_established(client, att_status);
1241021192e1SMilanka Ringwald                             hids_finalize_client(client);
1242021192e1SMilanka Ringwald                             return;
1243021192e1SMilanka Ringwald 
1244021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
1245021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1246021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1247021192e1SMilanka Ringwald                                 break;
1248021192e1SMilanka Ringwald                             }
1249021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1250021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1251021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1252021192e1SMilanka Ringwald                                     break;
1253021192e1SMilanka Ringwald                                 }
1254021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1255021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1256021192e1SMilanka Ringwald                                 return;
1257021192e1SMilanka Ringwald                             }
1258021192e1SMilanka Ringwald                             break;
1259021192e1SMilanka Ringwald                         default:
1260021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1261021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1262021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1263021192e1SMilanka Ringwald                                     break;
1264021192e1SMilanka Ringwald                                 }
1265021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1266021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1267021192e1SMilanka Ringwald                                 return;
1268021192e1SMilanka Ringwald                             }
1269021192e1SMilanka Ringwald                             break;
1270021192e1SMilanka Ringwald                     }
1271021192e1SMilanka Ringwald 
1272021192e1SMilanka Ringwald                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1273021192e1SMilanka Ringwald                         break;
1274021192e1SMilanka Ringwald                     }
12753322b222SMilanka Ringwald 
12767e1e6e7dSMilanka Ringwald                     // 1. we need to get HID Descriptor and
12777e1e6e7dSMilanka Ringwald                     // 2. get external Report characteristics if referenced from Report Map
12787e1e6e7dSMilanka Ringwald                     if (hids_client_report_map_query_init(client)){
127970093cf5SMilanka Ringwald                         break;
128070093cf5SMilanka Ringwald                     }
128170093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
128270093cf5SMilanka Ringwald                     hids_finalize_client(client);
12836bcfb631SMilanka Ringwald                     break;
12846bcfb631SMilanka Ringwald 
12853322b222SMilanka Ringwald 
128628da36a6SMilanka Ringwald                 // HID descriptor found
128728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1288e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1289e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1290e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1291e7bd2dbeSMilanka Ringwald                         break;
1292e7bd2dbeSMilanka Ringwald                     }
1293e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1294e7bd2dbeSMilanka Ringwald                     break;
1295e7bd2dbeSMilanka Ringwald 
129628da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1297e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12983322b222SMilanka Ringwald                     // go for next report map
12993322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1300e7bd2dbeSMilanka Ringwald                         break;
1301e7bd2dbeSMilanka Ringwald                     }
1302e7bd2dbeSMilanka Ringwald 
13033322b222SMilanka Ringwald                     // read UUIDS for external characteristics
13043322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1305e7bd2dbeSMilanka Ringwald                         break;
1306e7bd2dbeSMilanka Ringwald                     }
1307e7bd2dbeSMilanka Ringwald 
1308e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1309e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13107e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1311e7bd2dbeSMilanka Ringwald                         break;
1312e7bd2dbeSMilanka Ringwald                     }
1313e7bd2dbeSMilanka Ringwald 
1314e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1315e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1316e7bd2dbeSMilanka Ringwald                     break;
1317e7bd2dbeSMilanka Ringwald 
131828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1319e7bd2dbeSMilanka Ringwald                     // go for next map report
13203322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1321e7bd2dbeSMilanka Ringwald                         break;
1322e7bd2dbeSMilanka Ringwald                     }
1323e7bd2dbeSMilanka Ringwald 
13243322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
13253322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1326e7bd2dbeSMilanka Ringwald                     break;
1327e7bd2dbeSMilanka Ringwald 
13283322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1329e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1330e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13317e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1332e7bd2dbeSMilanka Ringwald                         break;
1333e7bd2dbeSMilanka Ringwald                     }
1334e7bd2dbeSMilanka Ringwald 
1335e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1336e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1337e7bd2dbeSMilanka Ringwald                     break;
1338e7bd2dbeSMilanka Ringwald 
133928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1340af2241c2SMilanka Ringwald                     if (client->handle != 0){
134128da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
134270093cf5SMilanka Ringwald                         break;
134370093cf5SMilanka Ringwald                     }
134470093cf5SMilanka Ringwald 
134570093cf5SMilanka Ringwald                     // go for next report
13467e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
134770093cf5SMilanka Ringwald                         break;
134870093cf5SMilanka Ringwald                     }
1349835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
135070093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
135170093cf5SMilanka Ringwald                     break;
135270093cf5SMilanka Ringwald 
135328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
135470093cf5SMilanka Ringwald                     // go for next report
13557e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
135670093cf5SMilanka Ringwald                         break;
135770093cf5SMilanka Ringwald                     }
1358021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1359021192e1SMilanka Ringwald                         break;
1360021192e1SMilanka Ringwald                     }
1361835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
136270093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
136370093cf5SMilanka Ringwald                     break;
136470093cf5SMilanka Ringwald 
1365021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1366021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13672901a9b7SMilanka Ringwald                         break;
13682901a9b7SMilanka Ringwald                     }
1369835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13706bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13716bcfb631SMilanka Ringwald                     break;
1372f4d3b82aSMilanka Ringwald 
1373*cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1374*cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1375*cd28e7b1SMilanka Ringwald                         break;
1376*cd28e7b1SMilanka Ringwald                     }
1377*cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1378*cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1379*cd28e7b1SMilanka Ringwald                     break;
1380*cd28e7b1SMilanka Ringwald 
1381835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1382835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1383835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1384835a13f1SMilanka Ringwald                     break;
1385835a13f1SMilanka Ringwald #endif
1386f4d3b82aSMilanka Ringwald 
1387af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
13886d6f7efcSMilanka Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1389f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1390f4d3b82aSMilanka Ringwald                     break;
1391f4d3b82aSMilanka Ringwald 
13926d6f7efcSMilanka Ringwald 
1393cf26c8fbSMilanka Ringwald                 default:
1394cf26c8fbSMilanka Ringwald                     break;
1395cf26c8fbSMilanka Ringwald             }
1396cf26c8fbSMilanka Ringwald             break;
1397cf26c8fbSMilanka Ringwald 
1398cf26c8fbSMilanka Ringwald         default:
1399cf26c8fbSMilanka Ringwald             break;
1400cf26c8fbSMilanka Ringwald     }
14016bcfb631SMilanka Ringwald 
14026bcfb631SMilanka Ringwald     if (client != NULL){
14036bcfb631SMilanka Ringwald         hids_run_for_client(client);
14046bcfb631SMilanka Ringwald     }
1405cf26c8fbSMilanka Ringwald }
1406cf26c8fbSMilanka Ringwald 
14076bcfb631SMilanka 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){
1408cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1409cf26c8fbSMilanka Ringwald 
1410cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1411cf26c8fbSMilanka Ringwald     if (client != NULL){
1412cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1413cf26c8fbSMilanka Ringwald     }
1414cf26c8fbSMilanka Ringwald 
1415cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1416cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1417cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1418cf26c8fbSMilanka Ringwald     }
1419cf26c8fbSMilanka Ringwald 
1420cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1421cf26c8fbSMilanka Ringwald     if (client == NULL) {
1422cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1423cf26c8fbSMilanka Ringwald     }
1424cf26c8fbSMilanka Ringwald 
14256bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1426cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1427cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1428cf26c8fbSMilanka Ringwald 
1429cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1430cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1431cf26c8fbSMilanka Ringwald }
1432cf26c8fbSMilanka Ringwald 
1433cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1434cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1435cf26c8fbSMilanka Ringwald     if (client == NULL){
1436cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1437cf26c8fbSMilanka Ringwald     }
1438cf26c8fbSMilanka Ringwald     // finalize connection
1439cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1440cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1441cf26c8fbSMilanka Ringwald }
1442cf26c8fbSMilanka Ringwald 
14436d6f7efcSMilanka Ringwald uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
14441624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14451624214bSMilanka Ringwald     if (client == NULL){
14461624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14471624214bSMilanka Ringwald     }
14481624214bSMilanka Ringwald 
14491624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14501624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14511624214bSMilanka Ringwald     }
14521624214bSMilanka Ringwald 
14536d6f7efcSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id(client, report_id);
145483d7ed1cSMilanka Ringwald 
14551624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14561624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14571624214bSMilanka Ringwald     }
14581624214bSMilanka Ringwald 
14591624214bSMilanka Ringwald     uint16_t mtu;
14601624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14611624214bSMilanka Ringwald 
14621624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14631624214bSMilanka Ringwald         return status;
14641624214bSMilanka Ringwald     }
14651624214bSMilanka Ringwald 
14661624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14671624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14681624214bSMilanka Ringwald     }
14691624214bSMilanka Ringwald 
14706d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1471021192e1SMilanka Ringwald     client->report_index = report_index;
14721624214bSMilanka Ringwald     client->report = report;
14731624214bSMilanka Ringwald     client->report_len = report_len;
14741624214bSMilanka Ringwald 
14751624214bSMilanka Ringwald     hids_run_for_client(client);
14761624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14771624214bSMilanka Ringwald }
14781624214bSMilanka Ringwald 
147983d7ed1cSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id){
148083d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
148183d7ed1cSMilanka Ringwald     if (client == NULL){
148283d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
148383d7ed1cSMilanka Ringwald     }
148483d7ed1cSMilanka Ringwald 
148583d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
148683d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
148783d7ed1cSMilanka Ringwald     }
148883d7ed1cSMilanka Ringwald 
1489835a13f1SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id(client, report_id);
149083d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
149183d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
149283d7ed1cSMilanka Ringwald     }
149383d7ed1cSMilanka Ringwald 
149483d7ed1cSMilanka Ringwald     client->report_index = report_index;
149583d7ed1cSMilanka Ringwald 
149683d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
149783d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
149883d7ed1cSMilanka Ringwald #else
149983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
150083d7ed1cSMilanka Ringwald #endif
150183d7ed1cSMilanka Ringwald     hids_run_for_client(client);
150283d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
150383d7ed1cSMilanka Ringwald }
150483d7ed1cSMilanka Ringwald 
150583d7ed1cSMilanka Ringwald 
1506f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1507f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1508f4d3b82aSMilanka Ringwald     if (client == NULL){
1509f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1510f4d3b82aSMilanka Ringwald     }
1511f4d3b82aSMilanka Ringwald 
1512f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1513f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1514f4d3b82aSMilanka Ringwald     }
1515f4d3b82aSMilanka Ringwald 
1516f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1517f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1518f4d3b82aSMilanka Ringwald     }
1519f4d3b82aSMilanka Ringwald 
1520f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1521af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1522af2241c2SMilanka Ringwald 
1523af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1524af2241c2SMilanka Ringwald     hids_run_for_client(client);
1525af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1526af2241c2SMilanka Ringwald }
1527af2241c2SMilanka Ringwald 
1528af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1529af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1530af2241c2SMilanka Ringwald     if (client == NULL){
1531af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1532af2241c2SMilanka Ringwald     }
1533af2241c2SMilanka Ringwald 
1534af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1535af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1536af2241c2SMilanka Ringwald     }
1537af2241c2SMilanka Ringwald 
1538af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1539af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1540af2241c2SMilanka Ringwald     }
1541af2241c2SMilanka Ringwald 
1542af2241c2SMilanka Ringwald     client->service_index = service_index;
1543af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1544af2241c2SMilanka Ringwald 
1545af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1546f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1547f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1548f4d3b82aSMilanka Ringwald }
1549f4d3b82aSMilanka Ringwald 
15506d6f7efcSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, hid_protocol_mode_t protocol_mode, uint8_t service_index){
15516d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15526d6f7efcSMilanka Ringwald     if (client == NULL){
15536d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15546d6f7efcSMilanka Ringwald     }
15556d6f7efcSMilanka Ringwald 
15566d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15576d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15586d6f7efcSMilanka Ringwald     }
15596d6f7efcSMilanka Ringwald 
15606d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15616d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15626d6f7efcSMilanka Ringwald     }
15636d6f7efcSMilanka Ringwald 
15646d6f7efcSMilanka Ringwald     client->service_index = service_index;
15656d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15666d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15676d6f7efcSMilanka Ringwald 
15686d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15696d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15706d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15716d6f7efcSMilanka Ringwald }
15726d6f7efcSMilanka Ringwald 
15736d6f7efcSMilanka Ringwald 
15746d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15756d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15766d6f7efcSMilanka Ringwald     if (client == NULL){
15776d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15786d6f7efcSMilanka Ringwald     }
15796d6f7efcSMilanka Ringwald 
15806d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15816d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15826d6f7efcSMilanka Ringwald     }
15836d6f7efcSMilanka Ringwald 
15846d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15856d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15866d6f7efcSMilanka Ringwald     }
15876d6f7efcSMilanka Ringwald 
15886d6f7efcSMilanka Ringwald     client->service_index = service_index;
15896d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15906d6f7efcSMilanka Ringwald     client->value = value;
15916d6f7efcSMilanka Ringwald 
15926d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15936d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15946d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15956d6f7efcSMilanka Ringwald }
15966d6f7efcSMilanka Ringwald 
15976d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15986d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15996d6f7efcSMilanka Ringwald }
16006d6f7efcSMilanka Ringwald 
16016d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
16026d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
16036d6f7efcSMilanka Ringwald }
16046d6f7efcSMilanka Ringwald 
1605*cd28e7b1SMilanka Ringwald uint8_t hids_client_send_enable_notifications(uint16_t hids_cid){
1606*cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1607*cd28e7b1SMilanka Ringwald     if (client == NULL){
1608*cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1609*cd28e7b1SMilanka Ringwald     }
1610*cd28e7b1SMilanka Ringwald 
1611*cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1612*cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1613*cd28e7b1SMilanka Ringwald     }
1614*cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1615*cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1616*cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1617*cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1618*cd28e7b1SMilanka Ringwald     }
1619*cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1620*cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1621*cd28e7b1SMilanka Ringwald }
1622*cd28e7b1SMilanka Ringwald 
1623*cd28e7b1SMilanka Ringwald uint8_t hids_client_send_disable_notifications(uint16_t hids_cid){
1624*cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1625*cd28e7b1SMilanka Ringwald     if (client == NULL){
1626*cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1627*cd28e7b1SMilanka Ringwald     }
1628*cd28e7b1SMilanka Ringwald 
1629*cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1630*cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1631*cd28e7b1SMilanka Ringwald     }
1632*cd28e7b1SMilanka Ringwald 
1633*cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1634*cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1635*cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1636*cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1637*cd28e7b1SMilanka Ringwald     }
1638*cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1639*cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1640*cd28e7b1SMilanka Ringwald }
16416d6f7efcSMilanka Ringwald 
1642021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1643021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1644021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1645021192e1SMilanka Ringwald }
1646cf26c8fbSMilanka Ringwald 
1647cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1648