xref: /btstack/src/ble/gatt-service/hids_client.c (revision 835a13f1ce0229519cd8ee0d0c65924ab20e68cf)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23fc975d0eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24fc975d0eSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/att_db.h"
53fc975d0eSMilanka Ringwald #include "ble/core.h"
54fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
55fc975d0eSMilanka Ringwald #include "ble/sm.h"
56cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
57fc975d0eSMilanka Ringwald #include "btstack_debug.h"
58fc975d0eSMilanka Ringwald #include "btstack_event.h"
59fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
60fc975d0eSMilanka Ringwald #include "gap.h"
61fc975d0eSMilanka Ringwald 
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6570093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6670093cf5SMilanka Ringwald 
67da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
68da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
69da142a6fSMilanka Ringwald 
70da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
71da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
72da142a6fSMilanka Ringwald 
73cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
74cf26c8fbSMilanka Ringwald 
75556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
76556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
77556456ccSMilanka Ringwald     switch (uuid){
78556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
79556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
80556456ccSMilanka Ringwald 
81556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
82556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
83556456ccSMilanka Ringwald 
84556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
85556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
86556456ccSMilanka Ringwald 
87556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
88556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
89556456ccSMilanka Ringwald 
90556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
91556456ccSMilanka Ringwald             return "REPORT";
92556456ccSMilanka Ringwald 
93556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94556456ccSMilanka Ringwald             return "REPORT_MAP";
95556456ccSMilanka Ringwald 
96556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
97556456ccSMilanka Ringwald             return "HID_INFORMATION";
98556456ccSMilanka Ringwald 
99556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
100556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
101556456ccSMilanka Ringwald         default:
102556456ccSMilanka Ringwald             return "UKNOWN";
103556456ccSMilanka Ringwald     }
104556456ccSMilanka Ringwald }
105556456ccSMilanka Ringwald #endif
106556456ccSMilanka Ringwald 
107da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
109da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
110da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
111da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
112da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
113da142a6fSMilanka Ringwald         return client;
114da142a6fSMilanka Ringwald     }
115da142a6fSMilanka Ringwald     return NULL;
116da142a6fSMilanka Ringwald }
117da142a6fSMilanka Ringwald 
118da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
120da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
121da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
122da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
123da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
124da142a6fSMilanka Ringwald         return client;
125da142a6fSMilanka Ringwald     }
126da142a6fSMilanka Ringwald     return NULL;
127da142a6fSMilanka Ringwald }
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald // START Descriptor Storage Util
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
133da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
134da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
135da142a6fSMilanka Ringwald     uint8_t i;
136da142a6fSMilanka Ringwald 
137da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
138da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
139da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
140da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
141da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
142da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
143da142a6fSMilanka Ringwald         }
144da142a6fSMilanka Ringwald     }
145da142a6fSMilanka Ringwald     return free_space;
146da142a6fSMilanka Ringwald }
147da142a6fSMilanka Ringwald 
148da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
150da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
151da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
155da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
156da142a6fSMilanka Ringwald 
157da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
159da142a6fSMilanka Ringwald     return true;
160da142a6fSMilanka Ringwald }
161da142a6fSMilanka Ringwald 
162da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
163da142a6fSMilanka Ringwald     uint8_t service_index = 0;
164da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
165da142a6fSMilanka Ringwald 
166da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
167da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
168da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
169da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
170da142a6fSMilanka Ringwald     }
171da142a6fSMilanka Ringwald 
172da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
173da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
174da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
175da142a6fSMilanka Ringwald 
176da142a6fSMilanka Ringwald     uint8_t i;
177da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
178da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
179da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
180da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
181da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
182da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
183da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
184da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
185da142a6fSMilanka Ringwald             }
186da142a6fSMilanka Ringwald         }
187da142a6fSMilanka Ringwald     }
188da142a6fSMilanka Ringwald }
189da142a6fSMilanka Ringwald 
190da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
191da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
192021192e1SMilanka Ringwald     if (client == NULL){
193021192e1SMilanka Ringwald         return NULL;
194021192e1SMilanka Ringwald     }
195021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
196da142a6fSMilanka Ringwald         return NULL;
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
199da142a6fSMilanka Ringwald }
200da142a6fSMilanka Ringwald 
201da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
202da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
203021192e1SMilanka Ringwald     if (client == NULL){
204021192e1SMilanka Ringwald         return 0;
205021192e1SMilanka Ringwald     }
206021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
207da142a6fSMilanka Ringwald         return 0;
208da142a6fSMilanka Ringwald     }
209da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
210da142a6fSMilanka Ringwald }
211da142a6fSMilanka Ringwald 
212da142a6fSMilanka Ringwald // END Descriptor Storage Util
213da142a6fSMilanka Ringwald 
214cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
215cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
216cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
217cf26c8fbSMilanka Ringwald     } else {
218cf26c8fbSMilanka Ringwald         hids_cid_counter++;
219cf26c8fbSMilanka Ringwald     }
220cf26c8fbSMilanka Ringwald     return hids_cid_counter;
221fc975d0eSMilanka Ringwald }
222fc975d0eSMilanka Ringwald 
223ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
224ab116b1cSMilanka Ringwald     uint8_t i;
225a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
226ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
227ab116b1cSMilanka Ringwald             return i;
228ab116b1cSMilanka Ringwald         }
229ab116b1cSMilanka Ringwald     }
230ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
231ab116b1cSMilanka Ringwald }
232ab116b1cSMilanka Ringwald 
23319146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
23419146789SMilanka Ringwald     uint8_t i;
23519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
23619146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
23719146789SMilanka Ringwald             return i;
23819146789SMilanka Ringwald         }
23919146789SMilanka Ringwald     }
24019146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
24119146789SMilanka Ringwald }
24219146789SMilanka Ringwald 
24319146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
24419146789SMilanka Ringwald     uint8_t i;
24519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24619146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
24719146789SMilanka Ringwald             return true;
24819146789SMilanka Ringwald         }
24919146789SMilanka Ringwald     }
25019146789SMilanka Ringwald     return false;
25119146789SMilanka Ringwald }
25219146789SMilanka Ringwald 
253*835a13f1SMilanka Ringwald static uint8_t find_report_index_for_report_id(hids_client_t * client, uint8_t report_id){
25483d7ed1cSMilanka Ringwald     uint8_t i;
255*835a13f1SMilanka 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                 }
262*835a13f1SMilanka 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                 }
273*835a13f1SMilanka 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 
28219146789SMilanka Ringwald 
283ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
284ab116b1cSMilanka Ringwald     uint8_t i;
28583d7ed1cSMilanka Ringwald     switch (client->protocol_mode){
28683d7ed1cSMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
287a381a464SMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
28883d7ed1cSMilanka Ringwald                 if (!client->reports[i].boot_report){
28983d7ed1cSMilanka Ringwald                     continue;
29083d7ed1cSMilanka Ringwald                 }
291ab116b1cSMilanka Ringwald                 if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
292ab116b1cSMilanka Ringwald                     return i;
293ab116b1cSMilanka Ringwald                 }
294ab116b1cSMilanka Ringwald             }
29583d7ed1cSMilanka Ringwald             break;
29683d7ed1cSMilanka Ringwald 
29783d7ed1cSMilanka Ringwald         default:
29883d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
29983d7ed1cSMilanka Ringwald                 if (client->reports[i].boot_report){
30083d7ed1cSMilanka Ringwald                     continue;
30183d7ed1cSMilanka Ringwald                 }
30283d7ed1cSMilanka Ringwald                 if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
30383d7ed1cSMilanka Ringwald                     return i;
30483d7ed1cSMilanka Ringwald                 }
30583d7ed1cSMilanka Ringwald             }
30683d7ed1cSMilanka Ringwald             break;
30783d7ed1cSMilanka Ringwald     }
308ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
309ab116b1cSMilanka Ringwald }
310ab116b1cSMilanka Ringwald 
311021192e1SMilanka 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){
312ab116b1cSMilanka Ringwald 
31319146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
314ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
3153322b222SMilanka Ringwald         return report_index;
316ab116b1cSMilanka Ringwald     }
31728da36a6SMilanka Ringwald     report_index = client->num_reports;
318ab116b1cSMilanka Ringwald 
31928da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
32028da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
32128da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
32228da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
323ab116b1cSMilanka Ringwald 
32428da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
32528da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
32628da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
327021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
32870093cf5SMilanka Ringwald 
329021192e1SMilanka 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);
330ab116b1cSMilanka Ringwald         client->num_reports++;
3313322b222SMilanka Ringwald         return report_index;
332ab116b1cSMilanka Ringwald     } else {
333ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3343322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
335ab116b1cSMilanka Ringwald     }
336ab116b1cSMilanka Ringwald }
337ab116b1cSMilanka Ringwald 
33819146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
339556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
340556456ccSMilanka Ringwald 
341556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
34219146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
343556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
34419146789SMilanka Ringwald 
345556456ccSMilanka Ringwald         client->num_external_reports++;
34619146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
347556456ccSMilanka Ringwald         return report_index;
348556456ccSMilanka Ringwald     } else {
349556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
350556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
35170093cf5SMilanka Ringwald     }
352556456ccSMilanka Ringwald }
353556456ccSMilanka Ringwald 
35470093cf5SMilanka Ringwald 
355021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
356021192e1SMilanka Ringwald     uint8_t i;
357021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
358021192e1SMilanka Ringwald         if (!client->reports[i].boot_report){
359021192e1SMilanka Ringwald             return true;
360ab116b1cSMilanka Ringwald         }
361021192e1SMilanka Ringwald     }
362021192e1SMilanka Ringwald     return false;
363021192e1SMilanka Ringwald }
364021192e1SMilanka Ringwald 
365021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
366021192e1SMilanka Ringwald     uint8_t i;
367021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
368021192e1SMilanka Ringwald         if (client->reports[i].boot_report){
369021192e1SMilanka Ringwald             return true;
370021192e1SMilanka Ringwald         }
371021192e1SMilanka Ringwald     }
372021192e1SMilanka Ringwald     return false;
373ab116b1cSMilanka Ringwald }
374ab116b1cSMilanka Ringwald 
3757e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3767e1e6e7dSMilanka Ringwald     uint8_t i;
3773322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3783322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
379556456ccSMilanka Ringwald             return i;
3803322b222SMilanka Ringwald         }
3813322b222SMilanka Ringwald     }
382556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
383556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3843322b222SMilanka Ringwald }
3853322b222SMilanka Ringwald 
3863322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3873322b222SMilanka Ringwald     client->service_index++;
3883322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
389556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3903322b222SMilanka Ringwald         return true;
3913322b222SMilanka Ringwald     }
3923322b222SMilanka Ringwald     return false;
3933322b222SMilanka Ringwald }
3943322b222SMilanka Ringwald 
3953322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3963322b222SMilanka Ringwald     client->service_index = 0;
3973322b222SMilanka Ringwald 
3983322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3993322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
4003322b222SMilanka Ringwald         return true;
4013322b222SMilanka Ringwald     }
4023322b222SMilanka Ringwald     return false;
4033322b222SMilanka Ringwald }
4043322b222SMilanka Ringwald 
4053322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
406021192e1SMilanka Ringwald     client->report_index++;
40719146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
4083322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
409e7bd2dbeSMilanka Ringwald         return true;
410e7bd2dbeSMilanka Ringwald     }
411e7bd2dbeSMilanka Ringwald     return false;
412e7bd2dbeSMilanka Ringwald }
413e7bd2dbeSMilanka Ringwald 
4143322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
415021192e1SMilanka Ringwald     client->report_index = 0;
41619146789SMilanka Ringwald     if (client->num_external_reports > 0){
4173322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
4187e1e6e7dSMilanka Ringwald         return true;
4197e1e6e7dSMilanka Ringwald     }
4207e1e6e7dSMilanka Ringwald     return false;
4217e1e6e7dSMilanka Ringwald }
4227e1e6e7dSMilanka Ringwald 
4237e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
4247e1e6e7dSMilanka Ringwald     uint8_t i;
4257e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
426021192e1SMilanka Ringwald     switch (client->protocol_mode){
427021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
428021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
42970093cf5SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
43070093cf5SMilanka Ringwald                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
4317e1e6e7dSMilanka Ringwald                     index = i;
432021192e1SMilanka Ringwald                     client->service_index = report.service_index;
433021192e1SMilanka Ringwald                 }
434021192e1SMilanka Ringwald             }
435021192e1SMilanka Ringwald             break;
436021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
437021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
438021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
439021192e1SMilanka Ringwald                 if (report.boot_report){
440021192e1SMilanka Ringwald                     index = i;
441021192e1SMilanka Ringwald                     client->service_index = report.service_index;
442021192e1SMilanka Ringwald                 }
443021192e1SMilanka Ringwald             }
444021192e1SMilanka Ringwald             break;
445021192e1SMilanka Ringwald         default:
4467e1e6e7dSMilanka Ringwald             break;
44770093cf5SMilanka Ringwald     }
448021192e1SMilanka Ringwald 
449021192e1SMilanka Ringwald     client->report_index = index;
4507e1e6e7dSMilanka Ringwald     return index;
4517e1e6e7dSMilanka Ringwald }
4527e1e6e7dSMilanka Ringwald 
4537e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
454021192e1SMilanka Ringwald     client->report_index++;
4557e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
45628da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
457e7bd2dbeSMilanka Ringwald         return true;
45870093cf5SMilanka Ringwald     }
4597e1e6e7dSMilanka Ringwald     return false;
4607e1e6e7dSMilanka Ringwald }
4617e1e6e7dSMilanka Ringwald 
4627e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
463021192e1SMilanka Ringwald     client->report_index = 0;
4647e1e6e7dSMilanka Ringwald 
4657e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
46628da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4677e1e6e7dSMilanka Ringwald         return true;
4687e1e6e7dSMilanka Ringwald     }
469e7bd2dbeSMilanka Ringwald     return false;
47070093cf5SMilanka Ringwald }
471ab116b1cSMilanka Ringwald 
472021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
473021192e1SMilanka Ringwald     uint8_t i;
474021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
475021192e1SMilanka Ringwald 
476021192e1SMilanka Ringwald     switch (client->protocol_mode){
477021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
478021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
479021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
480021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
481021192e1SMilanka Ringwald                     continue;
482021192e1SMilanka Ringwald                 }
483021192e1SMilanka Ringwald                 if (!report.boot_report){
484021192e1SMilanka Ringwald                     index = i;
485021192e1SMilanka Ringwald                 }
486021192e1SMilanka Ringwald             }
487021192e1SMilanka Ringwald             break;
488021192e1SMilanka Ringwald 
489021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
490021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
491021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
492021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
493021192e1SMilanka Ringwald                     continue;
494021192e1SMilanka Ringwald                 }
495021192e1SMilanka Ringwald                 if (report.boot_report){
496021192e1SMilanka Ringwald                     index = i;
497021192e1SMilanka Ringwald                 }
498021192e1SMilanka Ringwald             }
499021192e1SMilanka Ringwald             break;
500021192e1SMilanka Ringwald 
501021192e1SMilanka Ringwald         default:
502021192e1SMilanka Ringwald             break;
503021192e1SMilanka Ringwald     }
504021192e1SMilanka Ringwald 
505021192e1SMilanka Ringwald     client->report_index = index;
506021192e1SMilanka Ringwald     return index;
507021192e1SMilanka Ringwald }
508021192e1SMilanka Ringwald 
509021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
510021192e1SMilanka Ringwald     client->report_index++;
511021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
512021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
513021192e1SMilanka Ringwald         return true;
514021192e1SMilanka Ringwald     }
515021192e1SMilanka Ringwald     return false;
516021192e1SMilanka Ringwald }
517021192e1SMilanka Ringwald 
518021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
519021192e1SMilanka Ringwald     client->report_index = 0;
520021192e1SMilanka Ringwald 
521021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
522021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
523021192e1SMilanka Ringwald         return true;
524021192e1SMilanka Ringwald     }
525021192e1SMilanka Ringwald     return false;
526021192e1SMilanka Ringwald }
527021192e1SMilanka Ringwald 
528cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
529cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
530cf26c8fbSMilanka Ringwald     if (!client){
531cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
532cf26c8fbSMilanka Ringwald         return NULL;
533cf26c8fbSMilanka Ringwald     }
534cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
535cf26c8fbSMilanka Ringwald     client->cid = cid;
536cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
537fc975d0eSMilanka Ringwald 
538cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
539cf26c8fbSMilanka Ringwald     return client;
540fc975d0eSMilanka Ringwald }
541fc975d0eSMilanka Ringwald 
542cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
543021192e1SMilanka Ringwald     // stop listening
544021192e1SMilanka Ringwald     uint8_t i;
545021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
546021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
547021192e1SMilanka Ringwald     }
548021192e1SMilanka Ringwald 
549da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
550cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
551cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
552fc975d0eSMilanka Ringwald }
553cf26c8fbSMilanka Ringwald 
554cf26c8fbSMilanka Ringwald 
555cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5566bcfb631SMilanka Ringwald     uint8_t event[8];
557cf26c8fbSMilanka Ringwald     int pos = 0;
558cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
559cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
560cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
561cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
562cf26c8fbSMilanka Ringwald     pos += 2;
563cf26c8fbSMilanka Ringwald     event[pos++] = status;
5646bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
565cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
566cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
567cf26c8fbSMilanka Ringwald }
568cf26c8fbSMilanka Ringwald 
569021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
570021192e1SMilanka Ringwald     uint16_t pos = 0;
571021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
572021192e1SMilanka Ringwald     pos++;  // skip len
573021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
574021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
575021192e1SMilanka Ringwald     pos += 2;
576021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
577021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
578021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
579021192e1SMilanka Ringwald     pos += 2;
580021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
58183d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
58283d7ed1cSMilanka Ringwald 
583021192e1SMilanka Ringwald }
584021192e1SMilanka Ringwald 
58583d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
586021192e1SMilanka Ringwald     UNUSED(packet_type);
587021192e1SMilanka Ringwald     UNUSED(channel);
58883d7ed1cSMilanka Ringwald 
589021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
590021192e1SMilanka Ringwald 
591021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
592021192e1SMilanka Ringwald     btstack_assert(client != NULL);
593021192e1SMilanka Ringwald 
594021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
595021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
596021192e1SMilanka Ringwald         return;
597021192e1SMilanka Ringwald     }
598021192e1SMilanka Ringwald 
59983d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
600021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
60183d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
602021192e1SMilanka Ringwald }
603021192e1SMilanka Ringwald 
60483d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
60583d7ed1cSMilanka Ringwald     UNUSED(packet_type);
60683d7ed1cSMilanka Ringwald     UNUSED(channel);
60783d7ed1cSMilanka Ringwald 
60883d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
60983d7ed1cSMilanka Ringwald 
61083d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
61183d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
61283d7ed1cSMilanka Ringwald 
61383d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
61483d7ed1cSMilanka Ringwald         return;
61583d7ed1cSMilanka Ringwald     }
61683d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
61783d7ed1cSMilanka Ringwald 
61883d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
61983d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
62083d7ed1cSMilanka Ringwald         return;
62183d7ed1cSMilanka Ringwald     }
62283d7ed1cSMilanka Ringwald 
62383d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
62483d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
62583d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
62683d7ed1cSMilanka Ringwald }
627cf26c8fbSMilanka Ringwald 
628cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
629cf26c8fbSMilanka Ringwald     uint8_t att_status;
6306bcfb631SMilanka Ringwald     gatt_client_service_t service;
6316bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
632cf26c8fbSMilanka Ringwald 
633cf26c8fbSMilanka Ringwald     switch (client->state){
634cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
635556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
636556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
637556456ccSMilanka Ringwald #endif
638cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
63919146789SMilanka Ringwald 
64019146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
641cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
642cf26c8fbSMilanka Ringwald             UNUSED(att_status);
643cf26c8fbSMilanka Ringwald             break;
644cf26c8fbSMilanka Ringwald 
645cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
646556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
647556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
648556456ccSMilanka Ringwald #endif
6496bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6506bcfb631SMilanka Ringwald 
6516bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6526bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
65319146789SMilanka Ringwald 
65419146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6556bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6566bcfb631SMilanka Ringwald 
6576bcfb631SMilanka Ringwald             UNUSED(att_status);
6586bcfb631SMilanka Ringwald             break;
6596bcfb631SMilanka Ringwald 
660021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
6612901a9b7SMilanka 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);
6626bcfb631SMilanka Ringwald             UNUSED(att_status);
6632901a9b7SMilanka Ringwald 
6642901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
665021192e1SMilanka Ringwald             if (hids_client_report_query_init(client)){
666021192e1SMilanka Ringwald                 break;
667021192e1SMilanka Ringwald             }
668021192e1SMilanka Ringwald 
669021192e1SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
670021192e1SMilanka Ringwald             hids_finalize_client(client);
671cf26c8fbSMilanka Ringwald             break;
672cf26c8fbSMilanka Ringwald 
67328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
674556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
675556456ccSMilanka 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);
676556456ccSMilanka Ringwald #endif
67728da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
678556456ccSMilanka Ringwald 
67919146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
68019146789SMilanka 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);
681e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
682e7bd2dbeSMilanka Ringwald             break;
683e7bd2dbeSMilanka Ringwald 
684e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
685556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
686556456ccSMilanka 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);
687556456ccSMilanka Ringwald #endif
688e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
689e7bd2dbeSMilanka Ringwald 
6903322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6913322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6923322b222SMilanka Ringwald 
69319146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
694e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
695e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
696e7bd2dbeSMilanka Ringwald             break;
697e7bd2dbeSMilanka Ringwald 
69828da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
699556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
70019146789SMilanka 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);
701556456ccSMilanka Ringwald #endif
70228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
703e7bd2dbeSMilanka Ringwald 
70419146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
70519146789SMilanka 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);
706e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
707e7bd2dbeSMilanka Ringwald             break;
708e7bd2dbeSMilanka Ringwald 
7093322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
710556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
71119146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
712556456ccSMilanka Ringwald #endif
7133322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
714e7bd2dbeSMilanka Ringwald 
7153322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7163322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
71719146789SMilanka Ringwald 
71819146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7193322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
720e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
721e7bd2dbeSMilanka Ringwald             break;
722e7bd2dbeSMilanka Ringwald 
72328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
724556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
72583d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
726556456ccSMilanka Ringwald                 client->report_index,
72783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
72883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
729556456ccSMilanka Ringwald #endif
73028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
73170093cf5SMilanka Ringwald             client->descriptor_handle = 0;
732556456ccSMilanka Ringwald 
733556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
734556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
735556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
736556456ccSMilanka Ringwald 
73719146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
73870093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
73970093cf5SMilanka Ringwald             UNUSED(att_status);
74070093cf5SMilanka Ringwald             break;
74170093cf5SMilanka Ringwald 
74228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
74328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
74470093cf5SMilanka Ringwald 
74519146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
74670093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
74770093cf5SMilanka Ringwald             client->descriptor_handle = 0;
74870093cf5SMilanka Ringwald             UNUSED(att_status);
74970093cf5SMilanka Ringwald             break;
75070093cf5SMilanka Ringwald 
751021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
752021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
753021192e1SMilanka Ringwald 
754021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
755021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
756021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
757021192e1SMilanka Ringwald 
75819146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
759021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
760021192e1SMilanka Ringwald 
761021192e1SMilanka Ringwald             if (att_status != ERROR_CODE_SUCCESS){
762021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
763021192e1SMilanka Ringwald                     hids_run_for_client(client);
764021192e1SMilanka Ringwald                     break;
765021192e1SMilanka Ringwald                 }
766021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
767021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
768021192e1SMilanka Ringwald             } else {
769021192e1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
770021192e1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
77183d7ed1cSMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
772021192e1SMilanka Ringwald 
773021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
774021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
775021192e1SMilanka Ringwald             }
776021192e1SMilanka Ringwald             UNUSED(att_status);
777021192e1SMilanka Ringwald             break;
778021192e1SMilanka Ringwald 
77983d7ed1cSMilanka Ringwald 
78083d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:
78183d7ed1cSMilanka Ringwald 
78283d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
78383d7ed1cSMilanka Ringwald             printf("    Send report [%d, %d, 0x%04X]:\n",
78483d7ed1cSMilanka Ringwald                 client->report_index,
78583d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
78683d7ed1cSMilanka Ringwald #endif
78783d7ed1cSMilanka Ringwald 
78883d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
78983d7ed1cSMilanka Ringwald 
79083d7ed1cSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
79183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
79283d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
79383d7ed1cSMilanka Ringwald             UNUSED(att_status);
79483d7ed1cSMilanka Ringwald             break;
79583d7ed1cSMilanka Ringwald 
79683d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
79783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
79883d7ed1cSMilanka Ringwald             printf("    Get report [ID %d, Service %d, handle 0x%04X]:\n",
79983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
80083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
80183d7ed1cSMilanka Ringwald #endif
80283d7ed1cSMilanka Ringwald 
80383d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
80483d7ed1cSMilanka Ringwald 
80583d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
80683d7ed1cSMilanka Ringwald                 &handle_report_event,
80783d7ed1cSMilanka Ringwald                 client->con_handle,
80883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
80983d7ed1cSMilanka Ringwald             UNUSED(att_status);
81083d7ed1cSMilanka Ringwald             break;
81183d7ed1cSMilanka Ringwald 
81283d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
81383d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
81483d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
81583d7ed1cSMilanka Ringwald 
81683d7ed1cSMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
81783d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
81883d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
81983d7ed1cSMilanka Ringwald                 client->con_handle,
82083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
82183d7ed1cSMilanka Ringwald 
82283d7ed1cSMilanka Ringwald             break;
82383d7ed1cSMilanka Ringwald #endif
82483d7ed1cSMilanka Ringwald 
8256bcfb631SMilanka Ringwald         default:
8266bcfb631SMilanka Ringwald             break;
8276bcfb631SMilanka Ringwald     }
8286bcfb631SMilanka Ringwald }
8296bcfb631SMilanka Ringwald 
830cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
831cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
832cf26c8fbSMilanka Ringwald     UNUSED(channel);
833cf26c8fbSMilanka Ringwald     UNUSED(size);
834cf26c8fbSMilanka Ringwald 
8356bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
836cf26c8fbSMilanka Ringwald     uint8_t att_status;
8376bcfb631SMilanka Ringwald     gatt_client_service_t service;
8386bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
83970093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
840ab116b1cSMilanka Ringwald 
841021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
842021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
843e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
8443322b222SMilanka Ringwald     uint8_t i;
8453322b222SMilanka Ringwald     uint8_t report_index;
846da142a6fSMilanka Ringwald 
8478cec2b74SMilanka Ringwald     const uint8_t * descriptor_value;
8488cec2b74SMilanka Ringwald     uint16_t descriptor_value_len;
849cf26c8fbSMilanka Ringwald 
850cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
851cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
852cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
853cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
854cf26c8fbSMilanka Ringwald 
8556bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
8566bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
8576bcfb631SMilanka Ringwald                 hids_finalize_client(client);
8586bcfb631SMilanka Ringwald                 break;
8596bcfb631SMilanka Ringwald             }
8606bcfb631SMilanka Ringwald 
861021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
862021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
8636bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
864021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
865021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
8666bcfb631SMilanka Ringwald                 client->num_instances++;
867021192e1SMilanka Ringwald 
868708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
869708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
870708c69d2SMilanka Ringwald #endif
871021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
872021192e1SMilanka Ringwald             }  else {
873021192e1SMilanka 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);
874da142a6fSMilanka Ringwald             }
8756bcfb631SMilanka Ringwald             break;
8766bcfb631SMilanka Ringwald 
8776bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
8786bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
8796bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
8806bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
88119146789SMilanka Ringwald 
88219146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
88319146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
88419146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
88519146789SMilanka Ringwald                     break;
88619146789SMilanka Ringwald                 }
88719146789SMilanka Ringwald             }
88819146789SMilanka Ringwald 
88919146789SMilanka Ringwald             switch (characteristic.uuid16){
89019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
89119146789SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
89219146789SMilanka Ringwald                     break;
89319146789SMilanka Ringwald 
89419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
89519146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
89619146789SMilanka Ringwald                     break;
89719146789SMilanka Ringwald 
89819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
89919146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
90019146789SMilanka Ringwald                     break;
90119146789SMilanka Ringwald 
90219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
90319146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
90419146789SMilanka Ringwald                     break;
90519146789SMilanka Ringwald 
90619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
90719146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
90819146789SMilanka Ringwald                     break;
90919146789SMilanka Ringwald 
91019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
91119146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
91219146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
91319146789SMilanka Ringwald                     break;
91419146789SMilanka Ringwald 
91519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
91619146789SMilanka Ringwald                     break;
91719146789SMilanka Ringwald 
91819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
91919146789SMilanka Ringwald                     break;
92019146789SMilanka Ringwald 
92119146789SMilanka Ringwald                 default:
92219146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
92319146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
92419146789SMilanka Ringwald #endif
92519146789SMilanka Ringwald                     return;
92619146789SMilanka Ringwald             }
92719146789SMilanka Ringwald 
92819146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
92919146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
93019146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
93119146789SMilanka Ringwald                 characteristic.start_handle,
93219146789SMilanka Ringwald                 characteristic.properties,
93319146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
93419146789SMilanka Ringwald                 client->service_index);
93519146789SMilanka Ringwald 
93619146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
93719146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
93819146789SMilanka Ringwald             }
93919146789SMilanka Ringwald             printf("\n");
94019146789SMilanka Ringwald #endif
941cf26c8fbSMilanka Ringwald             break;
942cf26c8fbSMilanka Ringwald 
9438cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
944e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
9458cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
946e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
947da142a6fSMilanka Ringwald 
9488cec2b74SMilanka Ringwald             descriptor_value = gatt_event_long_characteristic_value_query_result_get_value(packet);
9498cec2b74SMilanka Ringwald             descriptor_value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
9508cec2b74SMilanka Ringwald 
951556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9528cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
9538cec2b74SMilanka Ringwald             printf_hexdump(descriptor_value, descriptor_value_len);
954556456ccSMilanka Ringwald #endif
9558cec2b74SMilanka Ringwald             for (i = 0; i < descriptor_value_len; i++){
9568cec2b74SMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor_value[i]);
957da142a6fSMilanka Ringwald                 if (!stored){
958da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
959da142a6fSMilanka Ringwald                     break;
960da142a6fSMilanka Ringwald                 }
961da142a6fSMilanka Ringwald             }
962e7bd2dbeSMilanka Ringwald             break;
963e7bd2dbeSMilanka Ringwald 
96470093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
96570093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
96670093cf5SMilanka Ringwald             btstack_assert(client != NULL);
96770093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
96870093cf5SMilanka Ringwald 
969e7bd2dbeSMilanka Ringwald             switch (client->state) {
970e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
971e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
972e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
97319146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
9743322b222SMilanka Ringwald 
975556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
976556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
977556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
978556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
979556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
980556456ccSMilanka Ringwald                                 client->service_index, report_index);
981556456ccSMilanka Ringwald                         }
982556456ccSMilanka Ringwald #endif
983e7bd2dbeSMilanka Ringwald                     }
984e7bd2dbeSMilanka Ringwald                     break;
98528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
98670093cf5SMilanka Ringwald                     // setup for descriptor value query
98770093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
98870093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
989556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
990556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
991556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
992556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
99319146789SMilanka Ringwald #endif
994556456ccSMilanka Ringwald                     }
995556456ccSMilanka Ringwald 
99619146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
997556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
99883d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
999556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1000556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1001556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1002556456ccSMilanka Ringwald                     }
1003556456ccSMilanka Ringwald #endif
100470093cf5SMilanka Ringwald                     break;
1005556456ccSMilanka Ringwald 
1006e7bd2dbeSMilanka Ringwald                 default:
1007e7bd2dbeSMilanka Ringwald                     break;
1008e7bd2dbeSMilanka Ringwald             }
1009e7bd2dbeSMilanka Ringwald             break;
101070093cf5SMilanka Ringwald 
101183d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
101283d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
101383d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
101483d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
101583d7ed1cSMilanka Ringwald 
101683d7ed1cSMilanka Ringwald             printf("    Received CCC value: ");
101783d7ed1cSMilanka Ringwald             printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet),  gatt_event_characteristic_value_query_result_get_value_length(packet));
101883d7ed1cSMilanka Ringwald             break;
101983d7ed1cSMilanka Ringwald #endif
102083d7ed1cSMilanka Ringwald 
102170093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
102270093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
102370093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1024e7bd2dbeSMilanka Ringwald 
1025e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1026e7bd2dbeSMilanka Ringwald                 break;
1027e7bd2dbeSMilanka Ringwald             }
10287e1e6e7dSMilanka Ringwald 
1029e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1030e7bd2dbeSMilanka Ringwald             switch (client->state) {
103128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1032e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
103319146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
10343322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
103519146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1036556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103719146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
103819146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1039556456ccSMilanka Ringwald #endif
10403322b222SMilanka Ringwald                     }
1041e7bd2dbeSMilanka Ringwald                     break;
1042e7bd2dbeSMilanka Ringwald 
104328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
104419146789SMilanka Ringwald 
104519146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1046021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1047021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
104819146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
104983d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1050021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1051021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
105283d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
105319146789SMilanka Ringwald     #endif
105419146789SMilanka Ringwald                     }
1055e7bd2dbeSMilanka Ringwald                     break;
1056e7bd2dbeSMilanka Ringwald 
1057e7bd2dbeSMilanka Ringwald                 default:
1058e7bd2dbeSMilanka Ringwald                     break;
105970093cf5SMilanka Ringwald             }
106070093cf5SMilanka Ringwald             break;
106170093cf5SMilanka Ringwald 
1062cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1063cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1064cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1065cf26c8fbSMilanka Ringwald 
1066cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1067cf26c8fbSMilanka Ringwald 
1068cf26c8fbSMilanka Ringwald             switch (client->state){
1069cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
10706bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
10716bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
10726bcfb631SMilanka Ringwald                         hids_finalize_client(client);
1073cf26c8fbSMilanka Ringwald                         break;
10746bcfb631SMilanka Ringwald                     }
10756bcfb631SMilanka Ringwald 
10766bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
10776bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
10786bcfb631SMilanka Ringwald                         hids_finalize_client(client);
10796bcfb631SMilanka Ringwald                         break;
10806bcfb631SMilanka Ringwald                     }
10816bcfb631SMilanka Ringwald 
10826bcfb631SMilanka Ringwald                     client->service_index = 0;
108319146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
10846bcfb631SMilanka Ringwald                     break;
10856bcfb631SMilanka Ringwald 
10866bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
10876bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
10886bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
10896bcfb631SMilanka Ringwald                         hids_finalize_client(client);
10906bcfb631SMilanka Ringwald                         break;
10916bcfb631SMilanka Ringwald                     }
10926bcfb631SMilanka Ringwald 
109370093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
109470093cf5SMilanka Ringwald                         // discover characteristics of next service
109570093cf5SMilanka Ringwald                         client->service_index++;
109670093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1097021192e1SMilanka Ringwald                         break;
1098021192e1SMilanka Ringwald                     }
1099021192e1SMilanka Ringwald 
1100021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1101021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1102021192e1SMilanka Ringwald                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1103021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1104021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1105021192e1SMilanka Ringwald                                 break;
1106021192e1SMilanka Ringwald                             }
1107021192e1SMilanka Ringwald                             hids_emit_connection_established(client, att_status);
1108021192e1SMilanka Ringwald                             hids_finalize_client(client);
1109021192e1SMilanka Ringwald                             return;
1110021192e1SMilanka Ringwald 
1111021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
1112021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1113021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1114021192e1SMilanka Ringwald                                 break;
1115021192e1SMilanka Ringwald                             }
1116021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1117021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1118021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1119021192e1SMilanka Ringwald                                     break;
1120021192e1SMilanka Ringwald                                 }
1121021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1122021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1123021192e1SMilanka Ringwald                                 return;
1124021192e1SMilanka Ringwald                             }
1125021192e1SMilanka Ringwald                             break;
1126021192e1SMilanka Ringwald                         default:
1127021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1128021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1129021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1130021192e1SMilanka Ringwald                                     break;
1131021192e1SMilanka Ringwald                                 }
1132021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1133021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1134021192e1SMilanka Ringwald                                 return;
1135021192e1SMilanka Ringwald                             }
1136021192e1SMilanka Ringwald                             break;
1137021192e1SMilanka Ringwald                     }
1138021192e1SMilanka Ringwald 
1139021192e1SMilanka Ringwald                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1140021192e1SMilanka Ringwald                         break;
1141021192e1SMilanka Ringwald                     }
11423322b222SMilanka Ringwald 
11437e1e6e7dSMilanka Ringwald                     // 1. we need to get HID Descriptor and
11447e1e6e7dSMilanka Ringwald                     // 2. get external Report characteristics if referenced from Report Map
11457e1e6e7dSMilanka Ringwald                     if (hids_client_report_map_query_init(client)){
114670093cf5SMilanka Ringwald                         break;
114770093cf5SMilanka Ringwald                     }
114870093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
114970093cf5SMilanka Ringwald                     hids_finalize_client(client);
11506bcfb631SMilanka Ringwald                     break;
11516bcfb631SMilanka Ringwald 
11523322b222SMilanka Ringwald 
115328da36a6SMilanka Ringwald                 // HID descriptor found
115428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1155e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1156e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1157e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1158e7bd2dbeSMilanka Ringwald                         break;
1159e7bd2dbeSMilanka Ringwald                     }
1160e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1161e7bd2dbeSMilanka Ringwald                     break;
1162e7bd2dbeSMilanka Ringwald 
116328da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1164e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
11653322b222SMilanka Ringwald                     // go for next report map
11663322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1167e7bd2dbeSMilanka Ringwald                         break;
1168e7bd2dbeSMilanka Ringwald                     }
1169e7bd2dbeSMilanka Ringwald 
11703322b222SMilanka Ringwald                     // read UUIDS for external characteristics
11713322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1172e7bd2dbeSMilanka Ringwald                         break;
1173e7bd2dbeSMilanka Ringwald                     }
1174e7bd2dbeSMilanka Ringwald 
1175e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1176e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
11777e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1178e7bd2dbeSMilanka Ringwald                         break;
1179e7bd2dbeSMilanka Ringwald                     }
1180e7bd2dbeSMilanka Ringwald 
1181e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1182e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1183e7bd2dbeSMilanka Ringwald                     break;
1184e7bd2dbeSMilanka Ringwald 
118528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1186e7bd2dbeSMilanka Ringwald                     // go for next map report
11873322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1188e7bd2dbeSMilanka Ringwald                         break;
1189e7bd2dbeSMilanka Ringwald                     }
1190e7bd2dbeSMilanka Ringwald 
11913322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
11923322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1193e7bd2dbeSMilanka Ringwald                     break;
1194e7bd2dbeSMilanka Ringwald 
11953322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1196e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1197e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
11987e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1199e7bd2dbeSMilanka Ringwald                         break;
1200e7bd2dbeSMilanka Ringwald                     }
1201e7bd2dbeSMilanka Ringwald 
1202e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1203e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1204e7bd2dbeSMilanka Ringwald                     break;
1205e7bd2dbeSMilanka Ringwald 
120628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
120770093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
120828da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
120970093cf5SMilanka Ringwald                         break;
121070093cf5SMilanka Ringwald                     }
121170093cf5SMilanka Ringwald 
121270093cf5SMilanka Ringwald                     // go for next report
12137e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
121470093cf5SMilanka Ringwald                         break;
121570093cf5SMilanka Ringwald                     }
1216*835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
121770093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
121870093cf5SMilanka Ringwald                     break;
121970093cf5SMilanka Ringwald 
122028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
122170093cf5SMilanka Ringwald                     // go for next report
12227e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
122370093cf5SMilanka Ringwald                         break;
122470093cf5SMilanka Ringwald                     }
1225021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1226021192e1SMilanka Ringwald                         break;
1227021192e1SMilanka Ringwald                     }
1228*835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
122970093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
123070093cf5SMilanka Ringwald                     break;
123170093cf5SMilanka Ringwald 
1232021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1233021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
12342901a9b7SMilanka Ringwald                         break;
12352901a9b7SMilanka Ringwald                     }
1236*835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
12376bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
12386bcfb631SMilanka Ringwald                     break;
1239*835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1240*835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1241*835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1242*835a13f1SMilanka Ringwald                     break;
1243*835a13f1SMilanka Ringwald #endif
1244cf26c8fbSMilanka Ringwald                 default:
1245cf26c8fbSMilanka Ringwald                     break;
1246cf26c8fbSMilanka Ringwald             }
1247cf26c8fbSMilanka Ringwald             break;
1248cf26c8fbSMilanka Ringwald 
1249cf26c8fbSMilanka Ringwald         default:
1250cf26c8fbSMilanka Ringwald             break;
1251cf26c8fbSMilanka Ringwald     }
12526bcfb631SMilanka Ringwald 
12536bcfb631SMilanka Ringwald     if (client != NULL){
12546bcfb631SMilanka Ringwald         hids_run_for_client(client);
12556bcfb631SMilanka Ringwald     }
1256cf26c8fbSMilanka Ringwald }
1257cf26c8fbSMilanka Ringwald 
12586bcfb631SMilanka 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){
1259cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1260cf26c8fbSMilanka Ringwald 
1261cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1262cf26c8fbSMilanka Ringwald     if (client != NULL){
1263cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1264cf26c8fbSMilanka Ringwald     }
1265cf26c8fbSMilanka Ringwald 
1266cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1267cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1268cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1269cf26c8fbSMilanka Ringwald     }
1270cf26c8fbSMilanka Ringwald 
1271cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1272cf26c8fbSMilanka Ringwald     if (client == NULL) {
1273cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1274cf26c8fbSMilanka Ringwald     }
1275cf26c8fbSMilanka Ringwald 
12766bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1277cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1278cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1279cf26c8fbSMilanka Ringwald 
1280cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1281cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1282cf26c8fbSMilanka Ringwald }
1283cf26c8fbSMilanka Ringwald 
1284cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1285cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1286cf26c8fbSMilanka Ringwald     if (client == NULL){
1287cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1288cf26c8fbSMilanka Ringwald     }
1289cf26c8fbSMilanka Ringwald     // finalize connection
1290cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1291cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1292cf26c8fbSMilanka Ringwald }
1293cf26c8fbSMilanka Ringwald 
12941624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
12951624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
12961624214bSMilanka Ringwald     if (client == NULL){
12971624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
12981624214bSMilanka Ringwald     }
12991624214bSMilanka Ringwald 
13001624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
13011624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
13021624214bSMilanka Ringwald     }
13031624214bSMilanka Ringwald 
13042fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
130583d7ed1cSMilanka Ringwald 
13061624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
13071624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
13081624214bSMilanka Ringwald     }
13091624214bSMilanka Ringwald 
13101624214bSMilanka Ringwald     uint16_t mtu;
13111624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
13121624214bSMilanka Ringwald 
13131624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
13141624214bSMilanka Ringwald         return status;
13151624214bSMilanka Ringwald     }
13161624214bSMilanka Ringwald 
13171624214bSMilanka Ringwald     if (mtu - 2 < report_len){
13181624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
13191624214bSMilanka Ringwald     }
13201624214bSMilanka Ringwald 
13211624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1322021192e1SMilanka Ringwald     client->report_index = report_index;
13231624214bSMilanka Ringwald     client->report = report;
13241624214bSMilanka Ringwald     client->report_len = report_len;
13251624214bSMilanka Ringwald 
13261624214bSMilanka Ringwald     hids_run_for_client(client);
13271624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
13281624214bSMilanka Ringwald }
13291624214bSMilanka Ringwald 
133083d7ed1cSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id){
133183d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
133283d7ed1cSMilanka Ringwald     if (client == NULL){
133383d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
133483d7ed1cSMilanka Ringwald     }
133583d7ed1cSMilanka Ringwald 
133683d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
133783d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
133883d7ed1cSMilanka Ringwald     }
133983d7ed1cSMilanka Ringwald 
1340*835a13f1SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id(client, report_id);
134183d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
134283d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
134383d7ed1cSMilanka Ringwald     }
134483d7ed1cSMilanka Ringwald 
134583d7ed1cSMilanka Ringwald     client->report_index = report_index;
134683d7ed1cSMilanka Ringwald 
134783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
134883d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
134983d7ed1cSMilanka Ringwald #else
135083d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
135183d7ed1cSMilanka Ringwald #endif
135283d7ed1cSMilanka Ringwald     hids_run_for_client(client);
135383d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
135483d7ed1cSMilanka Ringwald }
135583d7ed1cSMilanka Ringwald 
135683d7ed1cSMilanka Ringwald 
1357021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1358021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1359021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1360021192e1SMilanka Ringwald }
1361cf26c8fbSMilanka Ringwald 
1362cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1363