xref: /btstack/src/ble/gatt-service/hids_client.c (revision f4d3b82a97d7d1bd03d0b27f0a50318d9f14b6d4)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23fc975d0eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24fc975d0eSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/att_db.h"
53fc975d0eSMilanka Ringwald #include "ble/core.h"
54fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
55fc975d0eSMilanka Ringwald #include "ble/sm.h"
56cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
57fc975d0eSMilanka Ringwald #include "btstack_debug.h"
58fc975d0eSMilanka Ringwald #include "btstack_event.h"
59fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
60fc975d0eSMilanka Ringwald #include "gap.h"
61fc975d0eSMilanka Ringwald 
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6570093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6670093cf5SMilanka Ringwald 
67da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
68da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
69da142a6fSMilanka Ringwald 
70da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
71da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
72da142a6fSMilanka Ringwald 
73cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
74cf26c8fbSMilanka Ringwald 
75556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
76556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
77556456ccSMilanka Ringwald     switch (uuid){
78556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
79556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
80556456ccSMilanka Ringwald 
81556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
82556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
83556456ccSMilanka Ringwald 
84556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
85556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
86556456ccSMilanka Ringwald 
87556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
88556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
89556456ccSMilanka Ringwald 
90556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
91556456ccSMilanka Ringwald             return "REPORT";
92556456ccSMilanka Ringwald 
93556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94556456ccSMilanka Ringwald             return "REPORT_MAP";
95556456ccSMilanka Ringwald 
96556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
97556456ccSMilanka Ringwald             return "HID_INFORMATION";
98556456ccSMilanka Ringwald 
99556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
100556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
101556456ccSMilanka Ringwald         default:
102556456ccSMilanka Ringwald             return "UKNOWN";
103556456ccSMilanka Ringwald     }
104556456ccSMilanka Ringwald }
105556456ccSMilanka Ringwald #endif
106556456ccSMilanka Ringwald 
107da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
109da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
110da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
111da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
112da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
113da142a6fSMilanka Ringwald         return client;
114da142a6fSMilanka Ringwald     }
115da142a6fSMilanka Ringwald     return NULL;
116da142a6fSMilanka Ringwald }
117da142a6fSMilanka Ringwald 
118da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
120da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
121da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
122da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
123da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
124da142a6fSMilanka Ringwald         return client;
125da142a6fSMilanka Ringwald     }
126da142a6fSMilanka Ringwald     return NULL;
127da142a6fSMilanka Ringwald }
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald // START Descriptor Storage Util
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
133da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
134da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
135da142a6fSMilanka Ringwald     uint8_t i;
136da142a6fSMilanka Ringwald 
137da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
138da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
139da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
140da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
141da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
142da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
143da142a6fSMilanka Ringwald         }
144da142a6fSMilanka Ringwald     }
145da142a6fSMilanka Ringwald     return free_space;
146da142a6fSMilanka Ringwald }
147da142a6fSMilanka Ringwald 
148da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
150da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
151da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
155da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
156da142a6fSMilanka Ringwald 
157da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
159da142a6fSMilanka Ringwald     return true;
160da142a6fSMilanka Ringwald }
161da142a6fSMilanka Ringwald 
162da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
163da142a6fSMilanka Ringwald     uint8_t service_index = 0;
164da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
165da142a6fSMilanka Ringwald 
166da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
167da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
168da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
169da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
170da142a6fSMilanka Ringwald     }
171da142a6fSMilanka Ringwald 
172da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
173da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
174da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
175da142a6fSMilanka Ringwald 
176da142a6fSMilanka Ringwald     uint8_t i;
177da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
178da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
179da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
180da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
181da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
182da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
183da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
184da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
185da142a6fSMilanka Ringwald             }
186da142a6fSMilanka Ringwald         }
187da142a6fSMilanka Ringwald     }
188da142a6fSMilanka Ringwald }
189da142a6fSMilanka Ringwald 
190da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
191da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
192021192e1SMilanka Ringwald     if (client == NULL){
193021192e1SMilanka Ringwald         return NULL;
194021192e1SMilanka Ringwald     }
195021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
196da142a6fSMilanka Ringwald         return NULL;
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
199da142a6fSMilanka Ringwald }
200da142a6fSMilanka Ringwald 
201da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
202da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
203021192e1SMilanka Ringwald     if (client == NULL){
204021192e1SMilanka Ringwald         return 0;
205021192e1SMilanka Ringwald     }
206021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
207da142a6fSMilanka Ringwald         return 0;
208da142a6fSMilanka Ringwald     }
209da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
210da142a6fSMilanka Ringwald }
211da142a6fSMilanka Ringwald 
212da142a6fSMilanka Ringwald // END Descriptor Storage Util
213da142a6fSMilanka Ringwald 
214cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
215cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
216cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
217cf26c8fbSMilanka Ringwald     } else {
218cf26c8fbSMilanka Ringwald         hids_cid_counter++;
219cf26c8fbSMilanka Ringwald     }
220cf26c8fbSMilanka Ringwald     return hids_cid_counter;
221fc975d0eSMilanka Ringwald }
222fc975d0eSMilanka Ringwald 
223ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
224ab116b1cSMilanka Ringwald     uint8_t i;
225a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
226ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
227ab116b1cSMilanka Ringwald             return i;
228ab116b1cSMilanka Ringwald         }
229ab116b1cSMilanka Ringwald     }
230ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
231ab116b1cSMilanka Ringwald }
232ab116b1cSMilanka Ringwald 
23319146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
23419146789SMilanka Ringwald     uint8_t i;
23519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
23619146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
23719146789SMilanka Ringwald             return i;
23819146789SMilanka Ringwald         }
23919146789SMilanka Ringwald     }
24019146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
24119146789SMilanka Ringwald }
24219146789SMilanka Ringwald 
24319146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
24419146789SMilanka Ringwald     uint8_t i;
24519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24619146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
24719146789SMilanka Ringwald             return true;
24819146789SMilanka Ringwald         }
24919146789SMilanka Ringwald     }
25019146789SMilanka Ringwald     return false;
25119146789SMilanka Ringwald }
25219146789SMilanka Ringwald 
253835a13f1SMilanka Ringwald static uint8_t find_report_index_for_report_id(hids_client_t * client, uint8_t report_id){
25483d7ed1cSMilanka Ringwald     uint8_t i;
255835a13f1SMilanka Ringwald 
25683d7ed1cSMilanka Ringwald     switch (client->protocol_mode){
25783d7ed1cSMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
25883d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
25983d7ed1cSMilanka Ringwald                 if (!client->reports[i].boot_report){
26083d7ed1cSMilanka Ringwald                     continue;
26183d7ed1cSMilanka Ringwald                 }
262835a13f1SMilanka Ringwald                 if (client->reports[i].report_id == report_id){
26383d7ed1cSMilanka Ringwald                     return i;
26483d7ed1cSMilanka Ringwald                 }
26583d7ed1cSMilanka Ringwald             }
26683d7ed1cSMilanka Ringwald             break;
26783d7ed1cSMilanka Ringwald 
26883d7ed1cSMilanka Ringwald         default:
26983d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
27083d7ed1cSMilanka Ringwald                 if (client->reports[i].boot_report){
27183d7ed1cSMilanka Ringwald                     continue;
27283d7ed1cSMilanka Ringwald                 }
273835a13f1SMilanka Ringwald                 if (client->reports[i].report_id == report_id){
27483d7ed1cSMilanka Ringwald                     return i;
27583d7ed1cSMilanka Ringwald                 }
27683d7ed1cSMilanka Ringwald             }
27783d7ed1cSMilanka Ringwald             break;
27883d7ed1cSMilanka Ringwald     }
27983d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
28083d7ed1cSMilanka Ringwald }
28119146789SMilanka Ringwald 
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 
585*f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
586*f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
587*f4d3b82aSMilanka Ringwald 
588*f4d3b82aSMilanka Ringwald     uint8_t event[11];
589*f4d3b82aSMilanka Ringwald     int pos = 0;
590*f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
591*f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
592*f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
593*f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
594*f4d3b82aSMilanka Ringwald     pos += 2;
595*f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
596*f4d3b82aSMilanka Ringwald 
597*f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
598*f4d3b82aSMilanka Ringwald     pos += 3;
599*f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
600*f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
601*f4d3b82aSMilanka Ringwald 
602*f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
603*f4d3b82aSMilanka Ringwald }
604*f4d3b82aSMilanka Ringwald 
605*f4d3b82aSMilanka Ringwald 
60683d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
607021192e1SMilanka Ringwald     UNUSED(packet_type);
608021192e1SMilanka Ringwald     UNUSED(channel);
60983d7ed1cSMilanka Ringwald 
610021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
611021192e1SMilanka Ringwald 
612021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
613021192e1SMilanka Ringwald     btstack_assert(client != NULL);
614021192e1SMilanka Ringwald 
615021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
616021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
617021192e1SMilanka Ringwald         return;
618021192e1SMilanka Ringwald     }
619021192e1SMilanka Ringwald 
62083d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
621021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
62283d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
623021192e1SMilanka Ringwald }
624021192e1SMilanka Ringwald 
62583d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
62683d7ed1cSMilanka Ringwald     UNUSED(packet_type);
62783d7ed1cSMilanka Ringwald     UNUSED(channel);
62883d7ed1cSMilanka Ringwald 
62983d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
63083d7ed1cSMilanka Ringwald 
63183d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
63283d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
63383d7ed1cSMilanka Ringwald 
63483d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
63583d7ed1cSMilanka Ringwald         return;
63683d7ed1cSMilanka Ringwald     }
63783d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
63883d7ed1cSMilanka Ringwald 
63983d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
64083d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
64183d7ed1cSMilanka Ringwald         return;
64283d7ed1cSMilanka Ringwald     }
64383d7ed1cSMilanka Ringwald 
64483d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
64583d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
64683d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
64783d7ed1cSMilanka Ringwald }
648cf26c8fbSMilanka Ringwald 
649cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
650cf26c8fbSMilanka Ringwald     uint8_t att_status;
6516bcfb631SMilanka Ringwald     gatt_client_service_t service;
6526bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
653cf26c8fbSMilanka Ringwald 
654cf26c8fbSMilanka Ringwald     switch (client->state){
655cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
656556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
657556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
658556456ccSMilanka Ringwald #endif
659cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
66019146789SMilanka Ringwald 
66119146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
662cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
663cf26c8fbSMilanka Ringwald             UNUSED(att_status);
664cf26c8fbSMilanka Ringwald             break;
665cf26c8fbSMilanka Ringwald 
666cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
667556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
668556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
669556456ccSMilanka Ringwald #endif
6706bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6716bcfb631SMilanka Ringwald 
6726bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6736bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
67419146789SMilanka Ringwald 
67519146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6766bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6776bcfb631SMilanka Ringwald 
6786bcfb631SMilanka Ringwald             UNUSED(att_status);
6796bcfb631SMilanka Ringwald             break;
6806bcfb631SMilanka Ringwald 
681021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
6822901a9b7SMilanka 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);
6836bcfb631SMilanka Ringwald             UNUSED(att_status);
6842901a9b7SMilanka Ringwald 
6852901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
686021192e1SMilanka Ringwald             if (hids_client_report_query_init(client)){
687021192e1SMilanka Ringwald                 break;
688021192e1SMilanka Ringwald             }
689021192e1SMilanka Ringwald 
690021192e1SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
691021192e1SMilanka Ringwald             hids_finalize_client(client);
692cf26c8fbSMilanka Ringwald             break;
693cf26c8fbSMilanka Ringwald 
69428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
695556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
696556456ccSMilanka 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);
697556456ccSMilanka Ringwald #endif
69828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
699556456ccSMilanka Ringwald 
70019146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
70119146789SMilanka 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);
702e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
703e7bd2dbeSMilanka Ringwald             break;
704e7bd2dbeSMilanka Ringwald 
705e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
706556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
707556456ccSMilanka 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);
708556456ccSMilanka Ringwald #endif
709e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
710e7bd2dbeSMilanka Ringwald 
7113322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
7123322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
7133322b222SMilanka Ringwald 
71419146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
715e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
716e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
717e7bd2dbeSMilanka Ringwald             break;
718e7bd2dbeSMilanka Ringwald 
71928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
720556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
72119146789SMilanka 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);
722556456ccSMilanka Ringwald #endif
72328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
724e7bd2dbeSMilanka Ringwald 
72519146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
72619146789SMilanka 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);
727e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
728e7bd2dbeSMilanka Ringwald             break;
729e7bd2dbeSMilanka Ringwald 
7303322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
731556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
73219146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
733556456ccSMilanka Ringwald #endif
7343322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
735e7bd2dbeSMilanka Ringwald 
7363322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7373322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
73819146789SMilanka Ringwald 
73919146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7403322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
741e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
742e7bd2dbeSMilanka Ringwald             break;
743e7bd2dbeSMilanka Ringwald 
74428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
745556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
74683d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
747556456ccSMilanka Ringwald                 client->report_index,
74883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
74983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
750556456ccSMilanka Ringwald #endif
75128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
75270093cf5SMilanka Ringwald             client->descriptor_handle = 0;
753556456ccSMilanka Ringwald 
754556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
755556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
756556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
757556456ccSMilanka Ringwald 
75819146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
75970093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
76070093cf5SMilanka Ringwald             UNUSED(att_status);
76170093cf5SMilanka Ringwald             break;
76270093cf5SMilanka Ringwald 
76328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
76428da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
76570093cf5SMilanka Ringwald 
76619146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
76770093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
76870093cf5SMilanka Ringwald             client->descriptor_handle = 0;
76970093cf5SMilanka Ringwald             UNUSED(att_status);
77070093cf5SMilanka Ringwald             break;
77170093cf5SMilanka Ringwald 
772021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
773021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
774021192e1SMilanka Ringwald 
775021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
776021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
777021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
778021192e1SMilanka Ringwald 
77919146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
780021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
781021192e1SMilanka Ringwald 
782021192e1SMilanka Ringwald             if (att_status != ERROR_CODE_SUCCESS){
783021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
784021192e1SMilanka Ringwald                     hids_run_for_client(client);
785021192e1SMilanka Ringwald                     break;
786021192e1SMilanka Ringwald                 }
787021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
788021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
789021192e1SMilanka Ringwald             } else {
790021192e1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
791021192e1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
79283d7ed1cSMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
793021192e1SMilanka Ringwald 
794021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
795021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
796021192e1SMilanka Ringwald             }
797021192e1SMilanka Ringwald             UNUSED(att_status);
798021192e1SMilanka Ringwald             break;
799021192e1SMilanka Ringwald 
80083d7ed1cSMilanka Ringwald 
80183d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:
80283d7ed1cSMilanka Ringwald 
80383d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
80483d7ed1cSMilanka Ringwald             printf("    Send report [%d, %d, 0x%04X]:\n",
80583d7ed1cSMilanka Ringwald                 client->report_index,
80683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
80783d7ed1cSMilanka Ringwald #endif
80883d7ed1cSMilanka Ringwald 
80983d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
81083d7ed1cSMilanka Ringwald 
81183d7ed1cSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
81283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
81383d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
81483d7ed1cSMilanka Ringwald             UNUSED(att_status);
81583d7ed1cSMilanka Ringwald             break;
81683d7ed1cSMilanka Ringwald 
81783d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
81883d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
819*f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
820*f4d3b82aSMilanka Ringwald                 client->report_index,
82183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
82283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82383d7ed1cSMilanka Ringwald #endif
82483d7ed1cSMilanka Ringwald 
82583d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
826*f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
82783d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
82883d7ed1cSMilanka Ringwald                 &handle_report_event,
82983d7ed1cSMilanka Ringwald                 client->con_handle,
83083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
83183d7ed1cSMilanka Ringwald             UNUSED(att_status);
83283d7ed1cSMilanka Ringwald             break;
83383d7ed1cSMilanka Ringwald 
83483d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
83583d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
83683d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
83783d7ed1cSMilanka Ringwald 
838*f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
83983d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
84083d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
84183d7ed1cSMilanka Ringwald                 client->con_handle,
84283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
84383d7ed1cSMilanka Ringwald 
84483d7ed1cSMilanka Ringwald             break;
84583d7ed1cSMilanka Ringwald #endif
846*f4d3b82aSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_HID_INFORMATION:
847*f4d3b82aSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_HID_INFORMATION_RESULT;
84883d7ed1cSMilanka Ringwald 
849*f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
850*f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
851*f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
852*f4d3b82aSMilanka Ringwald                 client->con_handle,
853*f4d3b82aSMilanka Ringwald                 client->services[client->service_index].hid_information_value_handle);
854*f4d3b82aSMilanka Ringwald             break;
8556bcfb631SMilanka Ringwald         default:
8566bcfb631SMilanka Ringwald             break;
8576bcfb631SMilanka Ringwald     }
8586bcfb631SMilanka Ringwald }
8596bcfb631SMilanka Ringwald 
860cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
861cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
862cf26c8fbSMilanka Ringwald     UNUSED(channel);
863cf26c8fbSMilanka Ringwald     UNUSED(size);
864cf26c8fbSMilanka Ringwald 
8656bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
866cf26c8fbSMilanka Ringwald     uint8_t att_status;
8676bcfb631SMilanka Ringwald     gatt_client_service_t service;
8686bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
86970093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
870ab116b1cSMilanka Ringwald 
871021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
872021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
873e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
8743322b222SMilanka Ringwald     uint8_t i;
8753322b222SMilanka Ringwald     uint8_t report_index;
876da142a6fSMilanka Ringwald 
877*f4d3b82aSMilanka Ringwald     const uint8_t * value;
878*f4d3b82aSMilanka Ringwald     uint16_t value_len;
879cf26c8fbSMilanka Ringwald 
880cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
881cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
882cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
883cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
884cf26c8fbSMilanka Ringwald 
8856bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
8866bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
8876bcfb631SMilanka Ringwald                 hids_finalize_client(client);
8886bcfb631SMilanka Ringwald                 break;
8896bcfb631SMilanka Ringwald             }
8906bcfb631SMilanka Ringwald 
891021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
892021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
8936bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
894021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
895021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
8966bcfb631SMilanka Ringwald                 client->num_instances++;
897021192e1SMilanka Ringwald 
898708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
899708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
900708c69d2SMilanka Ringwald #endif
901021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
902021192e1SMilanka Ringwald             }  else {
903021192e1SMilanka 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);
904da142a6fSMilanka Ringwald             }
9056bcfb631SMilanka Ringwald             break;
9066bcfb631SMilanka Ringwald 
9076bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9086bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
9096bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
9106bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
91119146789SMilanka Ringwald 
91219146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
91319146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
91419146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
91519146789SMilanka Ringwald                     break;
91619146789SMilanka Ringwald                 }
91719146789SMilanka Ringwald             }
91819146789SMilanka Ringwald 
91919146789SMilanka Ringwald             switch (characteristic.uuid16){
92019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
92119146789SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
92219146789SMilanka Ringwald                     break;
92319146789SMilanka Ringwald 
92419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
92519146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
92619146789SMilanka Ringwald                     break;
92719146789SMilanka Ringwald 
92819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
92919146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
93019146789SMilanka Ringwald                     break;
93119146789SMilanka Ringwald 
93219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
933*f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
93419146789SMilanka Ringwald                     break;
93519146789SMilanka Ringwald 
93619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
93719146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
93819146789SMilanka Ringwald                     break;
93919146789SMilanka Ringwald 
94019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94119146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
94219146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
94319146789SMilanka Ringwald                     break;
94419146789SMilanka Ringwald 
94519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
946*f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
947*f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_end_handle = characteristic.end_handle;
94819146789SMilanka Ringwald                     break;
94919146789SMilanka Ringwald 
95019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
951*f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
952*f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_end_handle = characteristic.end_handle;
95319146789SMilanka Ringwald                     break;
95419146789SMilanka Ringwald 
95519146789SMilanka Ringwald                 default:
95619146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
95719146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
95819146789SMilanka Ringwald #endif
95919146789SMilanka Ringwald                     return;
96019146789SMilanka Ringwald             }
96119146789SMilanka Ringwald 
96219146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
96319146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
96419146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
96519146789SMilanka Ringwald                 characteristic.start_handle,
96619146789SMilanka Ringwald                 characteristic.properties,
96719146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
96819146789SMilanka Ringwald                 client->service_index);
96919146789SMilanka Ringwald 
97019146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
97119146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
97219146789SMilanka Ringwald             }
97319146789SMilanka Ringwald             printf("\n");
97419146789SMilanka Ringwald #endif
975cf26c8fbSMilanka Ringwald             break;
976cf26c8fbSMilanka Ringwald 
9778cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
978e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
9798cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
980e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
981da142a6fSMilanka Ringwald 
982*f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
983*f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
9848cec2b74SMilanka Ringwald 
985556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9868cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
987*f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
988556456ccSMilanka Ringwald #endif
989*f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
990*f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
991da142a6fSMilanka Ringwald                 if (!stored){
992da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
993da142a6fSMilanka Ringwald                     break;
994da142a6fSMilanka Ringwald                 }
995da142a6fSMilanka Ringwald             }
996e7bd2dbeSMilanka Ringwald             break;
997e7bd2dbeSMilanka Ringwald 
99870093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
99970093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
100070093cf5SMilanka Ringwald             btstack_assert(client != NULL);
100170093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
100270093cf5SMilanka Ringwald 
1003e7bd2dbeSMilanka Ringwald             switch (client->state) {
1004e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1005e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1006e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
100719146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10083322b222SMilanka Ringwald 
1009556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1010556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1011556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1012556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1013556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1014556456ccSMilanka Ringwald                                 client->service_index, report_index);
1015556456ccSMilanka Ringwald                         }
1016556456ccSMilanka Ringwald #endif
1017e7bd2dbeSMilanka Ringwald                     }
1018e7bd2dbeSMilanka Ringwald                     break;
101928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
102070093cf5SMilanka Ringwald                     // setup for descriptor value query
102170093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
102270093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
1023556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1024556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1025556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1026556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
102719146789SMilanka Ringwald #endif
1028556456ccSMilanka Ringwald                     }
1029556456ccSMilanka Ringwald 
103019146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1031556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
103283d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1033556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1034556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1035556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1036556456ccSMilanka Ringwald                     }
1037556456ccSMilanka Ringwald #endif
103870093cf5SMilanka Ringwald                     break;
1039556456ccSMilanka Ringwald 
1040e7bd2dbeSMilanka Ringwald                 default:
1041e7bd2dbeSMilanka Ringwald                     break;
1042e7bd2dbeSMilanka Ringwald             }
1043e7bd2dbeSMilanka Ringwald             break;
104470093cf5SMilanka Ringwald 
104583d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
104683d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
104783d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
104883d7ed1cSMilanka Ringwald 
1049*f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1050*f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1051*f4d3b82aSMilanka Ringwald 
1052*f4d3b82aSMilanka Ringwald             switch (client->state){
1053*f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1054*f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
105583d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1056*f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
105783d7ed1cSMilanka Ringwald                     break;
105883d7ed1cSMilanka Ringwald #endif
1059*f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_GET_HID_INFORMATION_RESULT:
1060*f4d3b82aSMilanka Ringwald                     hids_client_emit_hid_information_event(client, value, value_len);
1061*f4d3b82aSMilanka Ringwald                     break;
1062*f4d3b82aSMilanka Ringwald 
1063*f4d3b82aSMilanka Ringwald                 default:
1064*f4d3b82aSMilanka Ringwald                     break;
1065*f4d3b82aSMilanka Ringwald             }
1066*f4d3b82aSMilanka Ringwald 
1067*f4d3b82aSMilanka Ringwald             break;
106883d7ed1cSMilanka Ringwald 
106970093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
107070093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
107170093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1072e7bd2dbeSMilanka Ringwald 
1073e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1074e7bd2dbeSMilanka Ringwald                 break;
1075e7bd2dbeSMilanka Ringwald             }
10767e1e6e7dSMilanka Ringwald 
1077e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1078e7bd2dbeSMilanka Ringwald             switch (client->state) {
107928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1080e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
108119146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
10823322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
108319146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1084556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
108519146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
108619146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1087556456ccSMilanka Ringwald #endif
10883322b222SMilanka Ringwald                     }
1089e7bd2dbeSMilanka Ringwald                     break;
1090e7bd2dbeSMilanka Ringwald 
109128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
109219146789SMilanka Ringwald 
109319146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1094021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1095021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
109619146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
109783d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1098021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1099021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
110083d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
110119146789SMilanka Ringwald     #endif
110219146789SMilanka Ringwald                     }
1103e7bd2dbeSMilanka Ringwald                     break;
1104e7bd2dbeSMilanka Ringwald 
1105e7bd2dbeSMilanka Ringwald                 default:
1106e7bd2dbeSMilanka Ringwald                     break;
110770093cf5SMilanka Ringwald             }
110870093cf5SMilanka Ringwald             break;
110970093cf5SMilanka Ringwald 
1110cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1111cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1112cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1113cf26c8fbSMilanka Ringwald 
1114cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1115cf26c8fbSMilanka Ringwald 
1116cf26c8fbSMilanka Ringwald             switch (client->state){
1117cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
11186bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11196bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11206bcfb631SMilanka Ringwald                         hids_finalize_client(client);
1121cf26c8fbSMilanka Ringwald                         break;
11226bcfb631SMilanka Ringwald                     }
11236bcfb631SMilanka Ringwald 
11246bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
11256bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
11266bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11276bcfb631SMilanka Ringwald                         break;
11286bcfb631SMilanka Ringwald                     }
11296bcfb631SMilanka Ringwald 
11306bcfb631SMilanka Ringwald                     client->service_index = 0;
113119146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
11326bcfb631SMilanka Ringwald                     break;
11336bcfb631SMilanka Ringwald 
11346bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
11356bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11366bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11376bcfb631SMilanka Ringwald                         hids_finalize_client(client);
11386bcfb631SMilanka Ringwald                         break;
11396bcfb631SMilanka Ringwald                     }
11406bcfb631SMilanka Ringwald 
114170093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
114270093cf5SMilanka Ringwald                         // discover characteristics of next service
114370093cf5SMilanka Ringwald                         client->service_index++;
114470093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1145021192e1SMilanka Ringwald                         break;
1146021192e1SMilanka Ringwald                     }
1147021192e1SMilanka Ringwald 
1148021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1149021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1150021192e1SMilanka Ringwald                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1151021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1152021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1153021192e1SMilanka Ringwald                                 break;
1154021192e1SMilanka Ringwald                             }
1155021192e1SMilanka Ringwald                             hids_emit_connection_established(client, att_status);
1156021192e1SMilanka Ringwald                             hids_finalize_client(client);
1157021192e1SMilanka Ringwald                             return;
1158021192e1SMilanka Ringwald 
1159021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
1160021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1161021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1162021192e1SMilanka Ringwald                                 break;
1163021192e1SMilanka Ringwald                             }
1164021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1165021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1166021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1167021192e1SMilanka Ringwald                                     break;
1168021192e1SMilanka Ringwald                                 }
1169021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1170021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1171021192e1SMilanka Ringwald                                 return;
1172021192e1SMilanka Ringwald                             }
1173021192e1SMilanka Ringwald                             break;
1174021192e1SMilanka Ringwald                         default:
1175021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1176021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1177021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1178021192e1SMilanka Ringwald                                     break;
1179021192e1SMilanka Ringwald                                 }
1180021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1181021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1182021192e1SMilanka Ringwald                                 return;
1183021192e1SMilanka Ringwald                             }
1184021192e1SMilanka Ringwald                             break;
1185021192e1SMilanka Ringwald                     }
1186021192e1SMilanka Ringwald 
1187021192e1SMilanka Ringwald                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1188021192e1SMilanka Ringwald                         break;
1189021192e1SMilanka Ringwald                     }
11903322b222SMilanka Ringwald 
11917e1e6e7dSMilanka Ringwald                     // 1. we need to get HID Descriptor and
11927e1e6e7dSMilanka Ringwald                     // 2. get external Report characteristics if referenced from Report Map
11937e1e6e7dSMilanka Ringwald                     if (hids_client_report_map_query_init(client)){
119470093cf5SMilanka Ringwald                         break;
119570093cf5SMilanka Ringwald                     }
119670093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
119770093cf5SMilanka Ringwald                     hids_finalize_client(client);
11986bcfb631SMilanka Ringwald                     break;
11996bcfb631SMilanka Ringwald 
12003322b222SMilanka Ringwald 
120128da36a6SMilanka Ringwald                 // HID descriptor found
120228da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1203e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1204e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1205e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1206e7bd2dbeSMilanka Ringwald                         break;
1207e7bd2dbeSMilanka Ringwald                     }
1208e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1209e7bd2dbeSMilanka Ringwald                     break;
1210e7bd2dbeSMilanka Ringwald 
121128da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1212e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12133322b222SMilanka Ringwald                     // go for next report map
12143322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1215e7bd2dbeSMilanka Ringwald                         break;
1216e7bd2dbeSMilanka Ringwald                     }
1217e7bd2dbeSMilanka Ringwald 
12183322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12193322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1220e7bd2dbeSMilanka Ringwald                         break;
1221e7bd2dbeSMilanka Ringwald                     }
1222e7bd2dbeSMilanka Ringwald 
1223e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1224e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12257e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1226e7bd2dbeSMilanka Ringwald                         break;
1227e7bd2dbeSMilanka Ringwald                     }
1228e7bd2dbeSMilanka Ringwald 
1229e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1230e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1231e7bd2dbeSMilanka Ringwald                     break;
1232e7bd2dbeSMilanka Ringwald 
123328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1234e7bd2dbeSMilanka Ringwald                     // go for next map report
12353322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1236e7bd2dbeSMilanka Ringwald                         break;
1237e7bd2dbeSMilanka Ringwald                     }
1238e7bd2dbeSMilanka Ringwald 
12393322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12403322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1241e7bd2dbeSMilanka Ringwald                     break;
1242e7bd2dbeSMilanka Ringwald 
12433322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1244e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1245e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12467e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1247e7bd2dbeSMilanka Ringwald                         break;
1248e7bd2dbeSMilanka Ringwald                     }
1249e7bd2dbeSMilanka Ringwald 
1250e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1251e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1252e7bd2dbeSMilanka Ringwald                     break;
1253e7bd2dbeSMilanka Ringwald 
125428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
125570093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
125628da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
125770093cf5SMilanka Ringwald                         break;
125870093cf5SMilanka Ringwald                     }
125970093cf5SMilanka Ringwald 
126070093cf5SMilanka Ringwald                     // go for next report
12617e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
126270093cf5SMilanka Ringwald                         break;
126370093cf5SMilanka Ringwald                     }
1264835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
126570093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
126670093cf5SMilanka Ringwald                     break;
126770093cf5SMilanka Ringwald 
126828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
126970093cf5SMilanka Ringwald                     // go for next report
12707e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
127170093cf5SMilanka Ringwald                         break;
127270093cf5SMilanka Ringwald                     }
1273021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1274021192e1SMilanka Ringwald                         break;
1275021192e1SMilanka Ringwald                     }
1276835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
127770093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
127870093cf5SMilanka Ringwald                     break;
127970093cf5SMilanka Ringwald 
1280021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1281021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
12822901a9b7SMilanka Ringwald                         break;
12832901a9b7SMilanka Ringwald                     }
1284835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
12856bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
12866bcfb631SMilanka Ringwald                     break;
1287*f4d3b82aSMilanka Ringwald 
1288835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1289835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1290835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1291835a13f1SMilanka Ringwald                     break;
1292835a13f1SMilanka Ringwald #endif
1293*f4d3b82aSMilanka Ringwald 
1294*f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_GET_HID_INFORMATION_RESULT:
1295*f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1296*f4d3b82aSMilanka Ringwald                     break;
1297*f4d3b82aSMilanka Ringwald 
1298cf26c8fbSMilanka Ringwald                 default:
1299cf26c8fbSMilanka Ringwald                     break;
1300cf26c8fbSMilanka Ringwald             }
1301cf26c8fbSMilanka Ringwald             break;
1302cf26c8fbSMilanka Ringwald 
1303cf26c8fbSMilanka Ringwald         default:
1304cf26c8fbSMilanka Ringwald             break;
1305cf26c8fbSMilanka Ringwald     }
13066bcfb631SMilanka Ringwald 
13076bcfb631SMilanka Ringwald     if (client != NULL){
13086bcfb631SMilanka Ringwald         hids_run_for_client(client);
13096bcfb631SMilanka Ringwald     }
1310cf26c8fbSMilanka Ringwald }
1311cf26c8fbSMilanka Ringwald 
13126bcfb631SMilanka 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){
1313cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1314cf26c8fbSMilanka Ringwald 
1315cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1316cf26c8fbSMilanka Ringwald     if (client != NULL){
1317cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1318cf26c8fbSMilanka Ringwald     }
1319cf26c8fbSMilanka Ringwald 
1320cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1321cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1322cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1323cf26c8fbSMilanka Ringwald     }
1324cf26c8fbSMilanka Ringwald 
1325cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1326cf26c8fbSMilanka Ringwald     if (client == NULL) {
1327cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1328cf26c8fbSMilanka Ringwald     }
1329cf26c8fbSMilanka Ringwald 
13306bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1331cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1332cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1333cf26c8fbSMilanka Ringwald 
1334cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1335cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1336cf26c8fbSMilanka Ringwald }
1337cf26c8fbSMilanka Ringwald 
1338cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1339cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1340cf26c8fbSMilanka Ringwald     if (client == NULL){
1341cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1342cf26c8fbSMilanka Ringwald     }
1343cf26c8fbSMilanka Ringwald     // finalize connection
1344cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1345cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1346cf26c8fbSMilanka Ringwald }
1347cf26c8fbSMilanka Ringwald 
13481624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
13491624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
13501624214bSMilanka Ringwald     if (client == NULL){
13511624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
13521624214bSMilanka Ringwald     }
13531624214bSMilanka Ringwald 
13541624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
13551624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
13561624214bSMilanka Ringwald     }
13571624214bSMilanka Ringwald 
13582fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
135983d7ed1cSMilanka Ringwald 
13601624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
13611624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
13621624214bSMilanka Ringwald     }
13631624214bSMilanka Ringwald 
13641624214bSMilanka Ringwald     uint16_t mtu;
13651624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
13661624214bSMilanka Ringwald 
13671624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
13681624214bSMilanka Ringwald         return status;
13691624214bSMilanka Ringwald     }
13701624214bSMilanka Ringwald 
13711624214bSMilanka Ringwald     if (mtu - 2 < report_len){
13721624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
13731624214bSMilanka Ringwald     }
13741624214bSMilanka Ringwald 
13751624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1376021192e1SMilanka Ringwald     client->report_index = report_index;
13771624214bSMilanka Ringwald     client->report = report;
13781624214bSMilanka Ringwald     client->report_len = report_len;
13791624214bSMilanka Ringwald 
13801624214bSMilanka Ringwald     hids_run_for_client(client);
13811624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
13821624214bSMilanka Ringwald }
13831624214bSMilanka Ringwald 
138483d7ed1cSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id){
138583d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
138683d7ed1cSMilanka Ringwald     if (client == NULL){
138783d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
138883d7ed1cSMilanka Ringwald     }
138983d7ed1cSMilanka Ringwald 
139083d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
139183d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
139283d7ed1cSMilanka Ringwald     }
139383d7ed1cSMilanka Ringwald 
1394835a13f1SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id(client, report_id);
139583d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
139683d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
139783d7ed1cSMilanka Ringwald     }
139883d7ed1cSMilanka Ringwald 
139983d7ed1cSMilanka Ringwald     client->report_index = report_index;
140083d7ed1cSMilanka Ringwald 
140183d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
140283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
140383d7ed1cSMilanka Ringwald #else
140483d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
140583d7ed1cSMilanka Ringwald #endif
140683d7ed1cSMilanka Ringwald     hids_run_for_client(client);
140783d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
140883d7ed1cSMilanka Ringwald }
140983d7ed1cSMilanka Ringwald 
141083d7ed1cSMilanka Ringwald 
1411*f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1412*f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1413*f4d3b82aSMilanka Ringwald     if (client == NULL){
1414*f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1415*f4d3b82aSMilanka Ringwald     }
1416*f4d3b82aSMilanka Ringwald 
1417*f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1418*f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1419*f4d3b82aSMilanka Ringwald     }
1420*f4d3b82aSMilanka Ringwald 
1421*f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1422*f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1423*f4d3b82aSMilanka Ringwald     }
1424*f4d3b82aSMilanka Ringwald 
1425*f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1426*f4d3b82aSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_HID_INFORMATION;
1427*f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1428*f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1429*f4d3b82aSMilanka Ringwald }
1430*f4d3b82aSMilanka Ringwald 
1431021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1432021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1433021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1434021192e1SMilanka Ringwald }
1435cf26c8fbSMilanka Ringwald 
1436cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1437