xref: /btstack/src/ble/gatt-service/hids_client.c (revision 021192e149b66ee057b4c4cfc6c2ef699f2c22e5)
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 
42fc975d0eSMilanka Ringwald #include <stdint.h>
43fc975d0eSMilanka Ringwald #include <string.h>
44fc975d0eSMilanka Ringwald 
45cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
46fc975d0eSMilanka Ringwald 
47cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
48fc975d0eSMilanka Ringwald #include "ble/att_db.h"
49fc975d0eSMilanka Ringwald #include "ble/core.h"
50fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
51fc975d0eSMilanka Ringwald #include "ble/sm.h"
52cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
53fc975d0eSMilanka Ringwald #include "btstack_debug.h"
54fc975d0eSMilanka Ringwald #include "btstack_event.h"
55fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
56fc975d0eSMilanka Ringwald #include "gap.h"
57fc975d0eSMilanka Ringwald 
5870093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
5970093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6070093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6270093cf5SMilanka Ringwald 
63da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
64da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
65da142a6fSMilanka Ringwald 
66da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
67da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
68da142a6fSMilanka Ringwald 
69cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
70cf26c8fbSMilanka Ringwald 
71da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
72da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
73da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
74da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
75da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
76da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
77da142a6fSMilanka Ringwald         return client;
78da142a6fSMilanka Ringwald     }
79da142a6fSMilanka Ringwald     return NULL;
80da142a6fSMilanka Ringwald }
81da142a6fSMilanka Ringwald 
82da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
83da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
84da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
85da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
86da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
87da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
88da142a6fSMilanka Ringwald         return client;
89da142a6fSMilanka Ringwald     }
90da142a6fSMilanka Ringwald     return NULL;
91da142a6fSMilanka Ringwald }
92da142a6fSMilanka Ringwald 
93da142a6fSMilanka Ringwald 
94da142a6fSMilanka Ringwald // START Descriptor Storage Util
95da142a6fSMilanka Ringwald 
96da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
97da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
98da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
99da142a6fSMilanka Ringwald     uint8_t i;
100da142a6fSMilanka Ringwald 
101da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
102da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
103da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
104da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
105da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
106da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
107da142a6fSMilanka Ringwald         }
108da142a6fSMilanka Ringwald     }
109da142a6fSMilanka Ringwald     return free_space;
110da142a6fSMilanka Ringwald }
111da142a6fSMilanka Ringwald 
112da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
113da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
114da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
115da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
116da142a6fSMilanka Ringwald }
117da142a6fSMilanka Ringwald 
118da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
119da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
120da142a6fSMilanka Ringwald 
121da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
122da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
123da142a6fSMilanka Ringwald     return true;
124da142a6fSMilanka Ringwald }
125da142a6fSMilanka Ringwald 
126da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
127da142a6fSMilanka Ringwald     uint8_t service_index = 0;
128da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
131da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
132da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
133da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
134da142a6fSMilanka Ringwald     }
135da142a6fSMilanka Ringwald 
136da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
137da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
138da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
139da142a6fSMilanka Ringwald 
140da142a6fSMilanka Ringwald     uint8_t i;
141da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
142da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
143da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
144da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
145da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
146da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
147da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
148da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
149da142a6fSMilanka Ringwald             }
150da142a6fSMilanka Ringwald         }
151da142a6fSMilanka Ringwald     }
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
155da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
156*021192e1SMilanka Ringwald     if (client == NULL){
157*021192e1SMilanka Ringwald         return NULL;
158*021192e1SMilanka Ringwald     }
159*021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
160da142a6fSMilanka Ringwald         return NULL;
161da142a6fSMilanka Ringwald     }
162da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
163da142a6fSMilanka Ringwald }
164da142a6fSMilanka Ringwald 
165da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
166da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
167*021192e1SMilanka Ringwald     if (client == NULL){
168*021192e1SMilanka Ringwald         return 0;
169*021192e1SMilanka Ringwald     }
170*021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
171da142a6fSMilanka Ringwald         return 0;
172da142a6fSMilanka Ringwald     }
173da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
174da142a6fSMilanka Ringwald }
175da142a6fSMilanka Ringwald 
176da142a6fSMilanka Ringwald // END Descriptor Storage Util
177da142a6fSMilanka Ringwald 
178cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
179cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
180cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
181cf26c8fbSMilanka Ringwald     } else {
182cf26c8fbSMilanka Ringwald         hids_cid_counter++;
183cf26c8fbSMilanka Ringwald     }
184cf26c8fbSMilanka Ringwald     return hids_cid_counter;
185fc975d0eSMilanka Ringwald }
186fc975d0eSMilanka Ringwald 
187ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
188ab116b1cSMilanka Ringwald     uint8_t i;
189a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
190ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
191ab116b1cSMilanka Ringwald             return i;
192ab116b1cSMilanka Ringwald         }
193ab116b1cSMilanka Ringwald     }
194ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
195ab116b1cSMilanka Ringwald }
196ab116b1cSMilanka Ringwald 
197ab116b1cSMilanka 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){
198ab116b1cSMilanka Ringwald     uint8_t i;
199a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
200ab116b1cSMilanka Ringwald         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
201ab116b1cSMilanka Ringwald             return i;
202ab116b1cSMilanka Ringwald         }
203ab116b1cSMilanka Ringwald     }
204ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
205ab116b1cSMilanka Ringwald }
206ab116b1cSMilanka Ringwald 
207*021192e1SMilanka 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){
208ab116b1cSMilanka Ringwald 
209ab116b1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
210ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2113322b222SMilanka Ringwald         return report_index;
212ab116b1cSMilanka Ringwald     }
21328da36a6SMilanka Ringwald     report_index = client->num_reports;
214ab116b1cSMilanka Ringwald 
21528da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
21628da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
21728da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
21828da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
219ab116b1cSMilanka Ringwald 
22028da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
22128da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
22228da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
223*021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
22470093cf5SMilanka Ringwald 
2253322b222SMilanka Ringwald         // log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
226*021192e1SMilanka 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);
227ab116b1cSMilanka Ringwald         client->num_reports++;
2283322b222SMilanka Ringwald         return report_index;
229ab116b1cSMilanka Ringwald     } else {
230ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
2313322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
232ab116b1cSMilanka Ringwald     }
233ab116b1cSMilanka Ringwald }
234ab116b1cSMilanka Ringwald 
23570093cf5SMilanka Ringwald static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){
23670093cf5SMilanka Ringwald     characteristic->value_handle = client->reports[report_index].value_handle;
23770093cf5SMilanka Ringwald     characteristic->end_handle = client->reports[report_index].end_handle;
23870093cf5SMilanka Ringwald     characteristic->properties = client->reports[report_index].properties;
23970093cf5SMilanka Ringwald }
24070093cf5SMilanka Ringwald 
241*021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
242*021192e1SMilanka Ringwald     uint8_t i;
243*021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
244*021192e1SMilanka Ringwald         if (!client->reports[i].boot_report){
245*021192e1SMilanka Ringwald             return true;
246ab116b1cSMilanka Ringwald         }
247*021192e1SMilanka Ringwald     }
248*021192e1SMilanka Ringwald     return false;
249*021192e1SMilanka Ringwald }
250*021192e1SMilanka Ringwald 
251*021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
252*021192e1SMilanka Ringwald     uint8_t i;
253*021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
254*021192e1SMilanka Ringwald         if (client->reports[i].boot_report){
255*021192e1SMilanka Ringwald             return true;
256*021192e1SMilanka Ringwald         }
257*021192e1SMilanka Ringwald     }
258*021192e1SMilanka Ringwald     return false;
259ab116b1cSMilanka Ringwald }
260ab116b1cSMilanka Ringwald 
2617e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
2627e1e6e7dSMilanka Ringwald     uint8_t i;
2637e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
2643322b222SMilanka Ringwald 
2653322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
2663322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
2673322b222SMilanka Ringwald             index = i;
2683322b222SMilanka Ringwald             break;
2693322b222SMilanka Ringwald         }
2703322b222SMilanka Ringwald     }
2713322b222SMilanka Ringwald     client->service_index = index;
2723322b222SMilanka Ringwald     return index;
2733322b222SMilanka Ringwald }
2743322b222SMilanka Ringwald 
2753322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
2763322b222SMilanka Ringwald     client->service_index++;
2773322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
2783322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
2793322b222SMilanka Ringwald         return true;
2803322b222SMilanka Ringwald     }
2813322b222SMilanka Ringwald     return false;
2823322b222SMilanka Ringwald }
2833322b222SMilanka Ringwald 
2843322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
2853322b222SMilanka Ringwald     client->service_index = 0;
2863322b222SMilanka Ringwald 
2873322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
2883322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
2893322b222SMilanka Ringwald         return true;
2903322b222SMilanka Ringwald     }
2913322b222SMilanka Ringwald     return false;
2923322b222SMilanka Ringwald }
2933322b222SMilanka Ringwald 
2943322b222SMilanka Ringwald 
2953322b222SMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_uuid_index(hids_client_t * client){
2963322b222SMilanka Ringwald     uint8_t i;
2973322b222SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
2983322b222SMilanka Ringwald 
299*021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports; i++){
300e7bd2dbeSMilanka Ringwald         hids_client_report_t report = client->reports[i];
301e7bd2dbeSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
3027e1e6e7dSMilanka Ringwald             index = i;
3037e1e6e7dSMilanka Ringwald             break;
304e7bd2dbeSMilanka Ringwald         }
3057e1e6e7dSMilanka Ringwald     }
306*021192e1SMilanka Ringwald     client->report_index = index;
3077e1e6e7dSMilanka Ringwald     return index;
3087e1e6e7dSMilanka Ringwald }
3097e1e6e7dSMilanka Ringwald 
3103322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
311*021192e1SMilanka Ringwald     client->report_index++;
3123322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3133322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
314e7bd2dbeSMilanka Ringwald         return true;
315e7bd2dbeSMilanka Ringwald     }
316e7bd2dbeSMilanka Ringwald     return false;
317e7bd2dbeSMilanka Ringwald }
318e7bd2dbeSMilanka Ringwald 
3193322b222SMilanka Ringwald 
3203322b222SMilanka Ringwald 
3213322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
322*021192e1SMilanka Ringwald     client->report_index = 0;
32370093cf5SMilanka Ringwald 
3243322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3253322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3267e1e6e7dSMilanka Ringwald         return true;
3277e1e6e7dSMilanka Ringwald     }
3287e1e6e7dSMilanka Ringwald     return false;
3297e1e6e7dSMilanka Ringwald }
3307e1e6e7dSMilanka Ringwald 
3317e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3327e1e6e7dSMilanka Ringwald     uint8_t i;
3337e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
334*021192e1SMilanka Ringwald     switch (client->protocol_mode){
335*021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
336*021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
33770093cf5SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
33870093cf5SMilanka Ringwald                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3397e1e6e7dSMilanka Ringwald                     index = i;
340*021192e1SMilanka Ringwald                     client->service_index = report.service_index;
341*021192e1SMilanka Ringwald                 }
342*021192e1SMilanka Ringwald             }
343*021192e1SMilanka Ringwald             break;
344*021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
345*021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
346*021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
347*021192e1SMilanka Ringwald                 if (report.boot_report){
348*021192e1SMilanka Ringwald                     index = i;
349*021192e1SMilanka Ringwald                     client->service_index = report.service_index;
350*021192e1SMilanka Ringwald                 }
351*021192e1SMilanka Ringwald             }
352*021192e1SMilanka Ringwald             break;
353*021192e1SMilanka Ringwald         default:
3547e1e6e7dSMilanka Ringwald             break;
35570093cf5SMilanka Ringwald     }
356*021192e1SMilanka Ringwald 
357*021192e1SMilanka Ringwald     client->report_index = index;
3587e1e6e7dSMilanka Ringwald     return index;
3597e1e6e7dSMilanka Ringwald }
3607e1e6e7dSMilanka Ringwald 
3617e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
362*021192e1SMilanka Ringwald     client->report_index++;
3637e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
36428da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
365e7bd2dbeSMilanka Ringwald         return true;
36670093cf5SMilanka Ringwald     }
3677e1e6e7dSMilanka Ringwald     return false;
3687e1e6e7dSMilanka Ringwald }
3697e1e6e7dSMilanka Ringwald 
3707e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
371*021192e1SMilanka Ringwald     client->report_index = 0;
3727e1e6e7dSMilanka Ringwald 
3737e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
37428da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
3757e1e6e7dSMilanka Ringwald         return true;
3767e1e6e7dSMilanka Ringwald     }
377e7bd2dbeSMilanka Ringwald     return false;
37870093cf5SMilanka Ringwald }
379ab116b1cSMilanka Ringwald 
380*021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
381*021192e1SMilanka Ringwald     uint8_t i;
382*021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
383*021192e1SMilanka Ringwald 
384*021192e1SMilanka Ringwald     switch (client->protocol_mode){
385*021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
386*021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
387*021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
388*021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
389*021192e1SMilanka Ringwald                     continue;
390*021192e1SMilanka Ringwald                 }
391*021192e1SMilanka Ringwald                 if (!report.boot_report){
392*021192e1SMilanka Ringwald                     index = i;
393*021192e1SMilanka Ringwald                 }
394*021192e1SMilanka Ringwald             }
395*021192e1SMilanka Ringwald             break;
396*021192e1SMilanka Ringwald 
397*021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
398*021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
399*021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
400*021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
401*021192e1SMilanka Ringwald                     continue;
402*021192e1SMilanka Ringwald                 }
403*021192e1SMilanka Ringwald                 if (report.boot_report){
404*021192e1SMilanka Ringwald                     index = i;
405*021192e1SMilanka Ringwald                 }
406*021192e1SMilanka Ringwald             }
407*021192e1SMilanka Ringwald             break;
408*021192e1SMilanka Ringwald 
409*021192e1SMilanka Ringwald         default:
410*021192e1SMilanka Ringwald             break;
411*021192e1SMilanka Ringwald     }
412*021192e1SMilanka Ringwald 
413*021192e1SMilanka Ringwald     client->report_index = index;
414*021192e1SMilanka Ringwald     return index;
415*021192e1SMilanka Ringwald }
416*021192e1SMilanka Ringwald 
417*021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
418*021192e1SMilanka Ringwald     client->report_index++;
419*021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
420*021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
421*021192e1SMilanka Ringwald         return true;
422*021192e1SMilanka Ringwald     }
423*021192e1SMilanka Ringwald     return false;
424*021192e1SMilanka Ringwald }
425*021192e1SMilanka Ringwald 
426*021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
427*021192e1SMilanka Ringwald     client->report_index = 0;
428*021192e1SMilanka Ringwald 
429*021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
430*021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
431*021192e1SMilanka Ringwald         return true;
432*021192e1SMilanka Ringwald     }
433*021192e1SMilanka Ringwald     return false;
434*021192e1SMilanka Ringwald }
435*021192e1SMilanka Ringwald 
436cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
437cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
438cf26c8fbSMilanka Ringwald     if (!client){
439cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
440cf26c8fbSMilanka Ringwald         return NULL;
441cf26c8fbSMilanka Ringwald     }
442cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
443cf26c8fbSMilanka Ringwald     client->cid = cid;
444cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
445fc975d0eSMilanka Ringwald 
446cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
447cf26c8fbSMilanka Ringwald     return client;
448fc975d0eSMilanka Ringwald }
449fc975d0eSMilanka Ringwald 
450cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
451*021192e1SMilanka Ringwald     // stop listening
452*021192e1SMilanka Ringwald     uint8_t i;
453*021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
454*021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
455*021192e1SMilanka Ringwald     }
456*021192e1SMilanka Ringwald 
457da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
458cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
459cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
460fc975d0eSMilanka Ringwald }
461cf26c8fbSMilanka Ringwald 
462cf26c8fbSMilanka Ringwald 
463cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
4646bcfb631SMilanka Ringwald     uint8_t event[8];
465cf26c8fbSMilanka Ringwald     int pos = 0;
466cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
467cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
468cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
469cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
470cf26c8fbSMilanka Ringwald     pos += 2;
471cf26c8fbSMilanka Ringwald     event[pos++] = status;
4726bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
473cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
474cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
475cf26c8fbSMilanka Ringwald }
476cf26c8fbSMilanka Ringwald 
477*021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
478*021192e1SMilanka Ringwald     uint16_t pos = 0;
479*021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
480*021192e1SMilanka Ringwald     pos++;  // skip len
481*021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
482*021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
483*021192e1SMilanka Ringwald     pos += 2;
484*021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
485*021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
486*021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
487*021192e1SMilanka Ringwald     pos += 2;
488*021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
489*021192e1SMilanka Ringwald     buffer[1] = pos + report_len + 1 - 2;
490*021192e1SMilanka Ringwald }
491*021192e1SMilanka Ringwald 
492*021192e1SMilanka Ringwald static void handle_report_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
493*021192e1SMilanka Ringwald     UNUSED(packet_type);
494*021192e1SMilanka Ringwald     UNUSED(channel);
495*021192e1SMilanka Ringwald     UNUSED(size);
496*021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
497*021192e1SMilanka Ringwald 
498*021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
499*021192e1SMilanka Ringwald     btstack_assert(client != NULL);
500*021192e1SMilanka Ringwald 
501*021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
502*021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
503*021192e1SMilanka Ringwald         return;
504*021192e1SMilanka Ringwald     }
505*021192e1SMilanka Ringwald 
506*021192e1SMilanka Ringwald     uint8_t * in_place_event = &packet[-1];
507*021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
508*021192e1SMilanka Ringwald 
509*021192e1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size);
510*021192e1SMilanka Ringwald }
511*021192e1SMilanka Ringwald 
512cf26c8fbSMilanka Ringwald 
513cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
514cf26c8fbSMilanka Ringwald     uint8_t att_status;
5156bcfb631SMilanka Ringwald     gatt_client_service_t service;
5166bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
517cf26c8fbSMilanka Ringwald 
518cf26c8fbSMilanka Ringwald     switch (client->state){
519cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
520cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
521cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
522cf26c8fbSMilanka Ringwald             // TODO handle status
523cf26c8fbSMilanka Ringwald             UNUSED(att_status);
524cf26c8fbSMilanka Ringwald             break;
525cf26c8fbSMilanka Ringwald 
526cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
5276bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
5286bcfb631SMilanka Ringwald 
5296bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
5306bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
5316bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
5326bcfb631SMilanka Ringwald 
5336bcfb631SMilanka Ringwald             UNUSED(att_status);
5346bcfb631SMilanka Ringwald             break;
5356bcfb631SMilanka Ringwald 
536*021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
5372901a9b7SMilanka 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);
5386bcfb631SMilanka Ringwald             UNUSED(att_status);
5392901a9b7SMilanka Ringwald 
5402901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
541*021192e1SMilanka Ringwald             if (hids_client_report_query_init(client)){
542*021192e1SMilanka Ringwald                 break;
543*021192e1SMilanka Ringwald             }
544*021192e1SMilanka Ringwald 
545*021192e1SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
546*021192e1SMilanka Ringwald             hids_finalize_client(client);
547cf26c8fbSMilanka Ringwald             break;
548cf26c8fbSMilanka Ringwald 
5491624214bSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:{
5501624214bSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
5511624214bSMilanka Ringwald 
5521624214bSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
553*021192e1SMilanka Ringwald                 client->reports[client->report_index].value_handle,
5541624214bSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
5551624214bSMilanka Ringwald             UNUSED(att_status);
5561624214bSMilanka Ringwald             break;
5571624214bSMilanka Ringwald         }
55870093cf5SMilanka Ringwald 
55928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
56028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
561*021192e1SMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->services[client->service_index].report_map_value_handle);
562e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
563e7bd2dbeSMilanka Ringwald             break;
564e7bd2dbeSMilanka Ringwald 
565e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
566e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
567e7bd2dbeSMilanka Ringwald 
5683322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
5693322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
5703322b222SMilanka Ringwald 
571e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
572e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
573e7bd2dbeSMilanka Ringwald             break;
574e7bd2dbeSMilanka Ringwald 
57528da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
57628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
577e7bd2dbeSMilanka Ringwald 
578*021192e1SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->report_index].value_handle);
579e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
580e7bd2dbeSMilanka Ringwald             break;
581e7bd2dbeSMilanka Ringwald 
5823322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
5833322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
584e7bd2dbeSMilanka Ringwald 
5853322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
5863322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
5873322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
588e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
589e7bd2dbeSMilanka Ringwald             break;
590e7bd2dbeSMilanka Ringwald 
59128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
59228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
59370093cf5SMilanka Ringwald             client->descriptor_handle = 0;
594*021192e1SMilanka Ringwald             hids_client_get_characteristic_for_report_index(client, client->report_index, &characteristic);
59570093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
59670093cf5SMilanka Ringwald             UNUSED(att_status);
59770093cf5SMilanka Ringwald             break;
59870093cf5SMilanka Ringwald 
59928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
60028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
60170093cf5SMilanka Ringwald 
60270093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
60370093cf5SMilanka Ringwald             client->descriptor_handle = 0;
60470093cf5SMilanka Ringwald             UNUSED(att_status);
60570093cf5SMilanka Ringwald             break;
60670093cf5SMilanka Ringwald 
607*021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
608*021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
609*021192e1SMilanka Ringwald 
610*021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
611*021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
612*021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
613*021192e1SMilanka Ringwald 
614*021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
615*021192e1SMilanka Ringwald 
616*021192e1SMilanka Ringwald             if (att_status != ERROR_CODE_SUCCESS){
617*021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
618*021192e1SMilanka Ringwald                     hids_run_for_client(client);
619*021192e1SMilanka Ringwald                     break;
620*021192e1SMilanka Ringwald                 }
621*021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
622*021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
623*021192e1SMilanka Ringwald             } else {
624*021192e1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
625*021192e1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
626*021192e1SMilanka Ringwald                     &handle_report_hid_event, client->con_handle, &characteristic);
627*021192e1SMilanka Ringwald 
628*021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
629*021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
630*021192e1SMilanka Ringwald             }
631*021192e1SMilanka Ringwald             UNUSED(att_status);
632*021192e1SMilanka Ringwald             break;
633*021192e1SMilanka Ringwald 
6346bcfb631SMilanka Ringwald         default:
6356bcfb631SMilanka Ringwald             break;
6366bcfb631SMilanka Ringwald     }
6376bcfb631SMilanka Ringwald }
6386bcfb631SMilanka Ringwald 
6391624214bSMilanka Ringwald 
640cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
641cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
642cf26c8fbSMilanka Ringwald     UNUSED(channel);
643cf26c8fbSMilanka Ringwald     UNUSED(size);
644cf26c8fbSMilanka Ringwald 
6456bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
646cf26c8fbSMilanka Ringwald     uint8_t att_status;
6476bcfb631SMilanka Ringwald     gatt_client_service_t service;
6486bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
64970093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
650ab116b1cSMilanka Ringwald 
651*021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
652*021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
653e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
6543322b222SMilanka Ringwald     uint8_t i;
6553322b222SMilanka Ringwald     uint8_t report_index;
656da142a6fSMilanka Ringwald 
657da142a6fSMilanka Ringwald     const uint8_t * descriptor;
658da142a6fSMilanka Ringwald     uint16_t descriptor_len;
659cf26c8fbSMilanka Ringwald 
660cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
661cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
662cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
663cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
664cf26c8fbSMilanka Ringwald 
6656bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
6666bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
6676bcfb631SMilanka Ringwald                 hids_finalize_client(client);
6686bcfb631SMilanka Ringwald                 break;
6696bcfb631SMilanka Ringwald             }
6706bcfb631SMilanka Ringwald 
671*021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
672*021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
6736bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
674*021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
675*021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
6766bcfb631SMilanka Ringwald                 client->num_instances++;
677*021192e1SMilanka Ringwald 
678*021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
679*021192e1SMilanka Ringwald             }  else {
680*021192e1SMilanka 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);
681da142a6fSMilanka Ringwald             }
6826bcfb631SMilanka Ringwald             break;
6836bcfb631SMilanka Ringwald 
6846bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
6856bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
6866bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
6876bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
6883322b222SMilanka Ringwald 
6893322b222SMilanka Ringwald             switch (client->state){
6903322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
6913322b222SMilanka Ringwald                     // update value handle od external reports, set type to reserved so that will be inlcuded in ID,TYPE update in hte next step
6923322b222SMilanka Ringwald                     for (i = 0; i < client->num_reports; i++){
6933322b222SMilanka Ringwald                         if (client->reports[i].external_report_reference_uuid == characteristic.uuid16){
6943322b222SMilanka Ringwald                             client->reports[i].report_id = HID_REPORT_MODE_REPORT_ID;
6953322b222SMilanka Ringwald                             client->reports[i].report_type = HID_REPORT_TYPE_RESERVED;
6963322b222SMilanka Ringwald                             client->reports[i].properties = characteristic.properties;
6973322b222SMilanka Ringwald                             client->reports[i].value_handle = characteristic.value_handle;
6983322b222SMilanka Ringwald                             client->reports[i].end_handle = characteristic.end_handle;
6993322b222SMilanka Ringwald                         }
7003322b222SMilanka Ringwald                     }
7013322b222SMilanka Ringwald                     break;
7023322b222SMilanka Ringwald 
7033322b222SMilanka Ringwald                 default:
7046bcfb631SMilanka Ringwald                     switch (characteristic.uuid16){
70570093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
70670093cf5SMilanka Ringwald                             client->protocol_mode_value_handle = characteristic.value_handle;
70770093cf5SMilanka Ringwald                             break;
70870093cf5SMilanka Ringwald 
7096bcfb631SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
710*021192e1SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
7116bcfb631SMilanka Ringwald                             break;
7121624214bSMilanka Ringwald 
7136bcfb631SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
714*021192e1SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
7156bcfb631SMilanka Ringwald                             break;
7161624214bSMilanka Ringwald 
7171624214bSMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
718*021192e1SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
7191624214bSMilanka Ringwald                             break;
7201624214bSMilanka Ringwald 
72170093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
722*021192e1SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
72370093cf5SMilanka Ringwald                             break;
72470093cf5SMilanka Ringwald 
72570093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
7263322b222SMilanka Ringwald                             client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
7273322b222SMilanka Ringwald                             client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
72870093cf5SMilanka Ringwald                             break;
72970093cf5SMilanka Ringwald 
73070093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
73170093cf5SMilanka Ringwald                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
73270093cf5SMilanka Ringwald                             break;
73370093cf5SMilanka Ringwald 
73470093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
73570093cf5SMilanka Ringwald                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
73670093cf5SMilanka Ringwald                             break;
7376bcfb631SMilanka Ringwald                         default:
7386bcfb631SMilanka Ringwald                             break;
7396bcfb631SMilanka Ringwald                 }
7403322b222SMilanka Ringwald             }
7413322b222SMilanka Ringwald 
742cf26c8fbSMilanka Ringwald             break;
743cf26c8fbSMilanka Ringwald 
744e7bd2dbeSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
745e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
746e7bd2dbeSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
747e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
748da142a6fSMilanka Ringwald 
749da142a6fSMilanka Ringwald             descriptor_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
750da142a6fSMilanka Ringwald             descriptor = gatt_event_characteristic_value_query_result_get_value(packet);
751da142a6fSMilanka Ringwald 
752da142a6fSMilanka Ringwald             for (i = 0; i < descriptor_len; i++){
753da142a6fSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor[i]);
754da142a6fSMilanka Ringwald                 if (!stored){
755da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
756da142a6fSMilanka Ringwald                     break;
757da142a6fSMilanka Ringwald                 }
758da142a6fSMilanka Ringwald             }
759e7bd2dbeSMilanka Ringwald             break;
760e7bd2dbeSMilanka Ringwald 
76170093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
76270093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
76370093cf5SMilanka Ringwald             btstack_assert(client != NULL);
76470093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
76570093cf5SMilanka Ringwald 
766e7bd2dbeSMilanka Ringwald             switch (client->state) {
767e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
768e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
769e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
770*021192e1SMilanka Ringwald                         report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED, false);
7713322b222SMilanka Ringwald 
7723322b222SMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
7733322b222SMilanka Ringwald                             client->reports[report_index].value_handle = characteristic_descriptor.handle;
7743322b222SMilanka Ringwald                             client->reports[report_index].service_index = client->service_index;
7753322b222SMilanka Ringwald                         }
7763322b222SMilanka Ringwald 
777e7bd2dbeSMilanka Ringwald                     }
778e7bd2dbeSMilanka Ringwald                     break;
77928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
78070093cf5SMilanka Ringwald                     // setup for descriptor value query
78170093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
78270093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
78370093cf5SMilanka Ringwald                     }
78470093cf5SMilanka Ringwald                     break;
785e7bd2dbeSMilanka Ringwald                 default:
786e7bd2dbeSMilanka Ringwald                     break;
787e7bd2dbeSMilanka Ringwald             }
788e7bd2dbeSMilanka Ringwald             break;
78970093cf5SMilanka Ringwald 
79070093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
79170093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
79270093cf5SMilanka Ringwald             btstack_assert(client != NULL);
793e7bd2dbeSMilanka Ringwald 
794e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
795e7bd2dbeSMilanka Ringwald                 break;
796e7bd2dbeSMilanka Ringwald             }
7977e1e6e7dSMilanka Ringwald 
798e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
799e7bd2dbeSMilanka Ringwald             switch (client->state) {
80028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
801e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
8023322b222SMilanka Ringwald                     report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
8033322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
8043322b222SMilanka Ringwald                         client->reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
8053322b222SMilanka Ringwald                     }
806e7bd2dbeSMilanka Ringwald                     break;
807e7bd2dbeSMilanka Ringwald 
80828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
809*021192e1SMilanka Ringwald                     client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
810*021192e1SMilanka Ringwald                     client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
81128da36a6SMilanka Ringwald                     log_info("update index %d, id %d, type %d, value handle 0x%02x",
812*021192e1SMilanka Ringwald                         client->report_index,
813*021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id,
814*021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type,
815*021192e1SMilanka Ringwald                         client->reports[client->report_index].value_handle);
8167e1e6e7dSMilanka Ringwald 
817e7bd2dbeSMilanka Ringwald                     break;
818e7bd2dbeSMilanka Ringwald 
819e7bd2dbeSMilanka Ringwald                 default:
820e7bd2dbeSMilanka Ringwald                     break;
82170093cf5SMilanka Ringwald             }
82270093cf5SMilanka Ringwald             break;
82370093cf5SMilanka Ringwald 
824cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
825cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
826cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
827cf26c8fbSMilanka Ringwald 
828cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
829cf26c8fbSMilanka Ringwald 
830cf26c8fbSMilanka Ringwald             switch (client->state){
831cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
8326bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
8336bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
8346bcfb631SMilanka Ringwald                         hids_finalize_client(client);
835cf26c8fbSMilanka Ringwald                         break;
8366bcfb631SMilanka Ringwald                     }
8376bcfb631SMilanka Ringwald 
8386bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
8396bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
8406bcfb631SMilanka Ringwald                         hids_finalize_client(client);
8416bcfb631SMilanka Ringwald                         break;
8426bcfb631SMilanka Ringwald                     }
8436bcfb631SMilanka Ringwald 
8446bcfb631SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
8456bcfb631SMilanka Ringwald                     client->service_index = 0;
8466bcfb631SMilanka Ringwald                     break;
8476bcfb631SMilanka Ringwald 
8486bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
8496bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
8506bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
8516bcfb631SMilanka Ringwald                         hids_finalize_client(client);
8526bcfb631SMilanka Ringwald                         break;
8536bcfb631SMilanka Ringwald                     }
8546bcfb631SMilanka Ringwald 
85570093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
85670093cf5SMilanka Ringwald                         // discover characteristics of next service
85770093cf5SMilanka Ringwald                         client->service_index++;
85870093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
859*021192e1SMilanka Ringwald                         break;
860*021192e1SMilanka Ringwald                     }
861*021192e1SMilanka Ringwald 
862*021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
863*021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
864*021192e1SMilanka Ringwald                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
865*021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
866*021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
867*021192e1SMilanka Ringwald                                 break;
868*021192e1SMilanka Ringwald                             }
869*021192e1SMilanka Ringwald                             hids_emit_connection_established(client, att_status);
870*021192e1SMilanka Ringwald                             hids_finalize_client(client);
871*021192e1SMilanka Ringwald                             return;
872*021192e1SMilanka Ringwald 
873*021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
874*021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
875*021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
876*021192e1SMilanka Ringwald                                 break;
877*021192e1SMilanka Ringwald                             }
878*021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
879*021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
880*021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
881*021192e1SMilanka Ringwald                                     break;
882*021192e1SMilanka Ringwald                                 }
883*021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
884*021192e1SMilanka Ringwald                                 hids_finalize_client(client);
885*021192e1SMilanka Ringwald                                 return;
886*021192e1SMilanka Ringwald                             }
887*021192e1SMilanka Ringwald                             break;
888*021192e1SMilanka Ringwald                         default:
889*021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
890*021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
891*021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
892*021192e1SMilanka Ringwald                                     break;
893*021192e1SMilanka Ringwald                                 }
894*021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
895*021192e1SMilanka Ringwald                                 hids_finalize_client(client);
896*021192e1SMilanka Ringwald                                 return;
897*021192e1SMilanka Ringwald                             }
898*021192e1SMilanka Ringwald                             break;
899*021192e1SMilanka Ringwald                     }
900*021192e1SMilanka Ringwald 
901*021192e1SMilanka Ringwald                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
902*021192e1SMilanka Ringwald                         break;
903*021192e1SMilanka Ringwald                     }
9043322b222SMilanka Ringwald 
9057e1e6e7dSMilanka Ringwald                     // 1. we need to get HID Descriptor and
9067e1e6e7dSMilanka Ringwald                     // 2. get external Report characteristics if referenced from Report Map
9077e1e6e7dSMilanka Ringwald                     if (hids_client_report_map_query_init(client)){
90870093cf5SMilanka Ringwald                         break;
90970093cf5SMilanka Ringwald                     }
91070093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
91170093cf5SMilanka Ringwald                     hids_finalize_client(client);
9126bcfb631SMilanka Ringwald                     break;
9136bcfb631SMilanka Ringwald 
9143322b222SMilanka Ringwald 
91528da36a6SMilanka Ringwald                 // HID descriptor found
91628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
917e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
918e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
919e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
920e7bd2dbeSMilanka Ringwald                         break;
921e7bd2dbeSMilanka Ringwald                     }
922e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
923e7bd2dbeSMilanka Ringwald                     break;
924e7bd2dbeSMilanka Ringwald 
92528da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
926e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
9273322b222SMilanka Ringwald                     // go for next report map
9283322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
929e7bd2dbeSMilanka Ringwald                         break;
930e7bd2dbeSMilanka Ringwald                     }
931e7bd2dbeSMilanka Ringwald 
9323322b222SMilanka Ringwald                     // read UUIDS for external characteristics
9333322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
934e7bd2dbeSMilanka Ringwald                         break;
935e7bd2dbeSMilanka Ringwald                     }
936e7bd2dbeSMilanka Ringwald 
937e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
938e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
9397e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
940e7bd2dbeSMilanka Ringwald                         break;
941e7bd2dbeSMilanka Ringwald                     }
942e7bd2dbeSMilanka Ringwald 
943e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
944e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
945e7bd2dbeSMilanka Ringwald                     break;
946e7bd2dbeSMilanka Ringwald 
94728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
948e7bd2dbeSMilanka Ringwald                     // go for next map report
9493322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
950e7bd2dbeSMilanka Ringwald                         break;
951e7bd2dbeSMilanka Ringwald                     }
952e7bd2dbeSMilanka Ringwald 
9533322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
9543322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
955e7bd2dbeSMilanka Ringwald                     break;
956e7bd2dbeSMilanka Ringwald 
9573322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
958e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
959e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
9607e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
961e7bd2dbeSMilanka Ringwald                         break;
962e7bd2dbeSMilanka Ringwald                     }
963e7bd2dbeSMilanka Ringwald 
964e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
965e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
966e7bd2dbeSMilanka Ringwald                     break;
967e7bd2dbeSMilanka Ringwald 
96828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
96970093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
97070093cf5SMilanka Ringwald                         // descriptor found
97128da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
97270093cf5SMilanka Ringwald                         break;
97370093cf5SMilanka Ringwald                     }
97470093cf5SMilanka Ringwald 
97570093cf5SMilanka Ringwald                     // go for next report
9767e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
97770093cf5SMilanka Ringwald                         break;
97870093cf5SMilanka Ringwald                     }
97970093cf5SMilanka Ringwald 
98070093cf5SMilanka Ringwald                     // TODO continue with report mode
98170093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
98270093cf5SMilanka Ringwald                     break;
98370093cf5SMilanka Ringwald 
98428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
98570093cf5SMilanka Ringwald                     // go for next report
9867e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
98770093cf5SMilanka Ringwald                         break;
98870093cf5SMilanka Ringwald                     }
98970093cf5SMilanka Ringwald 
990*021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
991*021192e1SMilanka Ringwald                         break;
992*021192e1SMilanka Ringwald                     }
99370093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
99470093cf5SMilanka Ringwald                     break;
99570093cf5SMilanka Ringwald 
996*021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
997*021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
9982901a9b7SMilanka Ringwald                         break;
9992901a9b7SMilanka Ringwald                     }
10006bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
10016bcfb631SMilanka Ringwald                     break;
10026bcfb631SMilanka Ringwald 
1003cf26c8fbSMilanka Ringwald                 default:
1004cf26c8fbSMilanka Ringwald                     break;
1005cf26c8fbSMilanka Ringwald             }
1006cf26c8fbSMilanka Ringwald             break;
1007cf26c8fbSMilanka Ringwald 
1008cf26c8fbSMilanka Ringwald         default:
1009cf26c8fbSMilanka Ringwald             break;
1010cf26c8fbSMilanka Ringwald     }
10116bcfb631SMilanka Ringwald 
10126bcfb631SMilanka Ringwald     if (client != NULL){
10136bcfb631SMilanka Ringwald         hids_run_for_client(client);
10146bcfb631SMilanka Ringwald     }
1015cf26c8fbSMilanka Ringwald }
1016cf26c8fbSMilanka Ringwald 
10176bcfb631SMilanka 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){
1018cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1019cf26c8fbSMilanka Ringwald 
1020cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1021cf26c8fbSMilanka Ringwald     if (client != NULL){
1022cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1023cf26c8fbSMilanka Ringwald     }
1024cf26c8fbSMilanka Ringwald 
1025cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1026cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1027cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1028cf26c8fbSMilanka Ringwald     }
1029cf26c8fbSMilanka Ringwald 
1030cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1031cf26c8fbSMilanka Ringwald     if (client == NULL) {
1032cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1033cf26c8fbSMilanka Ringwald     }
1034cf26c8fbSMilanka Ringwald 
10356bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1036cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1037cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1038cf26c8fbSMilanka Ringwald 
1039cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1040cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1041cf26c8fbSMilanka Ringwald }
1042cf26c8fbSMilanka Ringwald 
1043cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1044cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1045cf26c8fbSMilanka Ringwald     if (client == NULL){
1046cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1047cf26c8fbSMilanka Ringwald     }
1048cf26c8fbSMilanka Ringwald     // finalize connection
1049cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1050cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1051cf26c8fbSMilanka Ringwald }
1052cf26c8fbSMilanka Ringwald 
10531624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
10541624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
10551624214bSMilanka Ringwald     if (client == NULL){
10561624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
10571624214bSMilanka Ringwald     }
10581624214bSMilanka Ringwald 
10591624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
10601624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
10611624214bSMilanka Ringwald     }
10621624214bSMilanka Ringwald 
10632fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
10641624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
10651624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
10661624214bSMilanka Ringwald     }
10671624214bSMilanka Ringwald 
10681624214bSMilanka Ringwald     uint16_t mtu;
10691624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
10701624214bSMilanka Ringwald 
10711624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
10721624214bSMilanka Ringwald         return status;
10731624214bSMilanka Ringwald     }
10741624214bSMilanka Ringwald 
10751624214bSMilanka Ringwald     if (mtu - 2 < report_len){
10761624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
10771624214bSMilanka Ringwald     }
10781624214bSMilanka Ringwald 
10791624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1080*021192e1SMilanka Ringwald     client->report_index = report_index;
10811624214bSMilanka Ringwald     client->report = report;
10821624214bSMilanka Ringwald     client->report_len = report_len;
10831624214bSMilanka Ringwald 
10841624214bSMilanka Ringwald     hids_run_for_client(client);
10851624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
10861624214bSMilanka Ringwald }
10871624214bSMilanka Ringwald 
1088*021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1089*021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1090*021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1091*021192e1SMilanka Ringwald }
1092cf26c8fbSMilanka Ringwald 
1093cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1094