xref: /btstack/src/ble/gatt-service/hids_client.c (revision a391be9a0d8cceaa4a6710610e7c5142645027d2)
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
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/core.h"
53fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
54cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
55fc975d0eSMilanka Ringwald #include "btstack_debug.h"
56fc975d0eSMilanka Ringwald #include "btstack_event.h"
57fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
58fc975d0eSMilanka Ringwald #include "gap.h"
59fc975d0eSMilanka Ringwald 
6070093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6470093cf5SMilanka Ringwald 
65da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
66da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
67da142a6fSMilanka Ringwald 
68da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
69da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
70da142a6fSMilanka Ringwald 
71cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
721fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context);
73cf26c8fbSMilanka Ringwald 
74556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
75556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
76556456ccSMilanka Ringwald     switch (uuid){
77556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
78556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
79556456ccSMilanka Ringwald 
80556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
81556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
82556456ccSMilanka Ringwald 
83556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
84556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
85556456ccSMilanka Ringwald 
86556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
87556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
88556456ccSMilanka Ringwald 
89556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
90556456ccSMilanka Ringwald             return "REPORT";
91556456ccSMilanka Ringwald 
92556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
93556456ccSMilanka Ringwald             return "REPORT_MAP";
94556456ccSMilanka Ringwald 
95556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
96556456ccSMilanka Ringwald             return "HID_INFORMATION";
97556456ccSMilanka Ringwald 
98556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
99556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
100556456ccSMilanka Ringwald         default:
101556456ccSMilanka Ringwald             return "UKNOWN";
102556456ccSMilanka Ringwald     }
103556456ccSMilanka Ringwald }
104556456ccSMilanka Ringwald #endif
105556456ccSMilanka Ringwald 
106da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
107da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
109da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
110da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
111da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
112da142a6fSMilanka Ringwald         return client;
113da142a6fSMilanka Ringwald     }
114da142a6fSMilanka Ringwald     return NULL;
115da142a6fSMilanka Ringwald }
116da142a6fSMilanka Ringwald 
117da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
118da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
120da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
121da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
122da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
123da142a6fSMilanka Ringwald         return client;
124da142a6fSMilanka Ringwald     }
125da142a6fSMilanka Ringwald     return NULL;
126da142a6fSMilanka Ringwald }
127da142a6fSMilanka Ringwald 
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald // START Descriptor Storage Util
130da142a6fSMilanka Ringwald 
131*a391be9aSMatthias Ringwald static uint16_t hids_client_descriptors_len(hids_client_t * client){
132*a391be9aSMatthias Ringwald     uint16_t descriptors_len = 0;
133*a391be9aSMatthias Ringwald     uint8_t service_index;
134*a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
135*a391be9aSMatthias Ringwald         descriptors_len += client->services[service_index].hid_descriptor_len;
136*a391be9aSMatthias Ringwald     }
137*a391be9aSMatthias Ringwald     return descriptors_len;
138*a391be9aSMatthias Ringwald }
139*a391be9aSMatthias Ringwald 
140da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
141da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
142da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
143da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
144da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
145da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
146da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
147*a391be9aSMatthias Ringwald         free_space -= hids_client_descriptors_len(client);
148da142a6fSMilanka Ringwald     }
149da142a6fSMilanka Ringwald     return free_space;
150da142a6fSMilanka Ringwald }
151da142a6fSMilanka Ringwald 
152da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
153*a391be9aSMatthias Ringwald     // reserve remaining space for this connection
154*a391be9aSMatthias Ringwald     uint16_t available_space = hids_client_descriptor_storage_get_available_space();
155da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
156*a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_max_len = available_space;
157*a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
158da142a6fSMilanka Ringwald }
159da142a6fSMilanka Ringwald 
160da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
161*a391be9aSMatthias Ringwald     // store single hid descriptor byte
162da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
163da142a6fSMilanka Ringwald 
164da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
165da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
166da142a6fSMilanka Ringwald     return true;
167da142a6fSMilanka Ringwald }
168da142a6fSMilanka Ringwald 
169da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
170*a391be9aSMatthias Ringwald     uint8_t service_index;
171da142a6fSMilanka Ringwald 
172*a391be9aSMatthias Ringwald     // calculate descriptors len
173*a391be9aSMatthias Ringwald     uint16_t descriptors_len = hids_client_descriptors_len(client);
174da142a6fSMilanka Ringwald 
175*a391be9aSMatthias Ringwald     if (descriptors_len > 0){
176*a391be9aSMatthias Ringwald         // move higher descriptors down
177*a391be9aSMatthias Ringwald         uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
178da142a6fSMilanka Ringwald         memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
179da142a6fSMilanka Ringwald                 &hids_client_descriptor_storage[next_offset],
180da142a6fSMilanka Ringwald                 hids_client_descriptor_storage_len - next_offset);
181da142a6fSMilanka Ringwald 
182*a391be9aSMatthias Ringwald         // fix descriptor offset of higher descriptors
183da142a6fSMilanka Ringwald         btstack_linked_list_iterator_t it;
184da142a6fSMilanka Ringwald         btstack_linked_list_iterator_init(&it, &clients);
185da142a6fSMilanka Ringwald         while (btstack_linked_list_iterator_has_next(&it)){
186da142a6fSMilanka Ringwald             hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
187*a391be9aSMatthias Ringwald             if (conn == client) continue;
188*a391be9aSMatthias Ringwald             for (service_index = 0; service_index < client->num_instances; service_index++){
189*a391be9aSMatthias Ringwald                 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
190*a391be9aSMatthias Ringwald                     conn->services[service_index].hid_descriptor_offset -= descriptors_len;
191da142a6fSMilanka Ringwald                 }
192da142a6fSMilanka Ringwald             }
193da142a6fSMilanka Ringwald         }
194da142a6fSMilanka Ringwald     }
195da142a6fSMilanka Ringwald 
196*a391be9aSMatthias Ringwald     // clear descriptors
197*a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
198*a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_len = 0;
199*a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_offset = 0;
200*a391be9aSMatthias Ringwald     }
201*a391be9aSMatthias Ringwald }
202*a391be9aSMatthias Ringwald 
203da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
204da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
205021192e1SMilanka Ringwald     if (client == NULL){
206021192e1SMilanka Ringwald         return NULL;
207021192e1SMilanka Ringwald     }
208021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
209da142a6fSMilanka Ringwald         return NULL;
210da142a6fSMilanka Ringwald     }
211da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
212da142a6fSMilanka Ringwald }
213da142a6fSMilanka Ringwald 
214da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
215da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
216021192e1SMilanka Ringwald     if (client == NULL){
217021192e1SMilanka Ringwald         return 0;
218021192e1SMilanka Ringwald     }
219021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
220da142a6fSMilanka Ringwald         return 0;
221da142a6fSMilanka Ringwald     }
222da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
223da142a6fSMilanka Ringwald }
224da142a6fSMilanka Ringwald 
225da142a6fSMilanka Ringwald // END Descriptor Storage Util
226da142a6fSMilanka Ringwald 
227cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
228cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
229cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
230cf26c8fbSMilanka Ringwald     } else {
231cf26c8fbSMilanka Ringwald         hids_cid_counter++;
232cf26c8fbSMilanka Ringwald     }
233cf26c8fbSMilanka Ringwald     return hids_cid_counter;
234fc975d0eSMilanka Ringwald }
235fc975d0eSMilanka Ringwald 
236ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
237ab116b1cSMilanka Ringwald     uint8_t i;
238a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
239ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
240ab116b1cSMilanka Ringwald             return i;
241ab116b1cSMilanka Ringwald         }
242ab116b1cSMilanka Ringwald     }
243ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
244ab116b1cSMilanka Ringwald }
245ab116b1cSMilanka Ringwald 
24619146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
24719146789SMilanka Ringwald     uint8_t i;
24819146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24919146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
25019146789SMilanka Ringwald             return i;
25119146789SMilanka Ringwald         }
25219146789SMilanka Ringwald     }
25319146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
25419146789SMilanka Ringwald }
25519146789SMilanka Ringwald 
25619146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
25719146789SMilanka Ringwald     uint8_t i;
25819146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
25919146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
26019146789SMilanka Ringwald             return true;
26119146789SMilanka Ringwald         }
26219146789SMilanka Ringwald     }
26319146789SMilanka Ringwald     return false;
26419146789SMilanka Ringwald }
26519146789SMilanka Ringwald 
266fd39e93aSMilanka Ringwald static uint8_t find_report_index_for_report_id_and_report_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
26783d7ed1cSMilanka Ringwald     uint8_t i;
268835a13f1SMilanka Ringwald 
26983d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
270fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
271fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
272fd39e93aSMilanka Ringwald 
273fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
27483d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
27583d7ed1cSMilanka Ringwald                 continue;
27683d7ed1cSMilanka Ringwald             }
277fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
27883d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
27983d7ed1cSMilanka Ringwald                 continue;
28083d7ed1cSMilanka Ringwald             }
281fd39e93aSMilanka Ringwald         }
282fd39e93aSMilanka Ringwald 
283fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
28483d7ed1cSMilanka Ringwald             return i;
28583d7ed1cSMilanka Ringwald         }
28683d7ed1cSMilanka Ringwald     }
28783d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
28883d7ed1cSMilanka Ringwald }
28919146789SMilanka Ringwald 
290021192e1SMilanka 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){
291ab116b1cSMilanka Ringwald 
29219146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
293ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2943322b222SMilanka Ringwald         return report_index;
295ab116b1cSMilanka Ringwald     }
29628da36a6SMilanka Ringwald     report_index = client->num_reports;
297ab116b1cSMilanka Ringwald 
29828da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
29928da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
30028da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
30128da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
302ab116b1cSMilanka Ringwald 
30328da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
30428da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
30528da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
306021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
30770093cf5SMilanka Ringwald 
308021192e1SMilanka 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);
309ab116b1cSMilanka Ringwald         client->num_reports++;
3103322b222SMilanka Ringwald         return report_index;
311ab116b1cSMilanka Ringwald     } else {
312ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3133322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
314ab116b1cSMilanka Ringwald     }
315ab116b1cSMilanka Ringwald }
316ab116b1cSMilanka Ringwald 
31719146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
318556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
319556456ccSMilanka Ringwald 
320556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
32119146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
322556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
32319146789SMilanka Ringwald 
324556456ccSMilanka Ringwald         client->num_external_reports++;
32519146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
326556456ccSMilanka Ringwald         return report_index;
327556456ccSMilanka Ringwald     } else {
328556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
329556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
33070093cf5SMilanka Ringwald     }
331556456ccSMilanka Ringwald }
332556456ccSMilanka Ringwald 
3337e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3347e1e6e7dSMilanka Ringwald     uint8_t i;
3353322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3363322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
337556456ccSMilanka Ringwald             return i;
3383322b222SMilanka Ringwald         }
3393322b222SMilanka Ringwald     }
340556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
341556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3423322b222SMilanka Ringwald }
3433322b222SMilanka Ringwald 
3443322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3453322b222SMilanka Ringwald     client->service_index++;
3463322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
347556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3483322b222SMilanka Ringwald         return true;
3493322b222SMilanka Ringwald     }
3503322b222SMilanka Ringwald     return false;
3513322b222SMilanka Ringwald }
3523322b222SMilanka Ringwald 
3533322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3543322b222SMilanka Ringwald     client->service_index = 0;
3553322b222SMilanka Ringwald 
3563322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3573322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3583322b222SMilanka Ringwald         return true;
3593322b222SMilanka Ringwald     }
3603322b222SMilanka Ringwald     return false;
3613322b222SMilanka Ringwald }
3623322b222SMilanka Ringwald 
3633322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
364021192e1SMilanka Ringwald     client->report_index++;
36519146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3663322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
367e7bd2dbeSMilanka Ringwald         return true;
368e7bd2dbeSMilanka Ringwald     }
369e7bd2dbeSMilanka Ringwald     return false;
370e7bd2dbeSMilanka Ringwald }
371e7bd2dbeSMilanka Ringwald 
3723322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
373021192e1SMilanka Ringwald     client->report_index = 0;
37419146789SMilanka Ringwald     if (client->num_external_reports > 0){
3753322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3767e1e6e7dSMilanka Ringwald         return true;
3777e1e6e7dSMilanka Ringwald     }
3787e1e6e7dSMilanka Ringwald     return false;
3797e1e6e7dSMilanka Ringwald }
3807e1e6e7dSMilanka Ringwald 
3817e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3827e1e6e7dSMilanka Ringwald     uint8_t i;
3837e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
384fd39e93aSMilanka Ringwald 
385021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
38670093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
387fd39e93aSMilanka Ringwald         if (!report.boot_report){
38870093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3897e1e6e7dSMilanka Ringwald                 index = i;
390021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3917e1e6e7dSMilanka Ringwald                 break;
39270093cf5SMilanka Ringwald             }
393fd39e93aSMilanka Ringwald         }
394fd39e93aSMilanka Ringwald     }
395021192e1SMilanka Ringwald     client->report_index = index;
3967e1e6e7dSMilanka Ringwald     return index;
3977e1e6e7dSMilanka Ringwald }
3987e1e6e7dSMilanka Ringwald 
3997e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
400021192e1SMilanka Ringwald     client->report_index++;
4017e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
40228da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
403e7bd2dbeSMilanka Ringwald         return true;
40470093cf5SMilanka Ringwald     }
4057e1e6e7dSMilanka Ringwald     return false;
4067e1e6e7dSMilanka Ringwald }
4077e1e6e7dSMilanka Ringwald 
4087e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
409021192e1SMilanka Ringwald     client->report_index = 0;
4107e1e6e7dSMilanka Ringwald 
4117e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
41228da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4137e1e6e7dSMilanka Ringwald         return true;
4147e1e6e7dSMilanka Ringwald     }
415e7bd2dbeSMilanka Ringwald     return false;
41670093cf5SMilanka Ringwald }
417ab116b1cSMilanka Ringwald 
418021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
419021192e1SMilanka Ringwald     uint8_t i;
420021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
421021192e1SMilanka Ringwald 
422021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
423021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
424fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
425fd39e93aSMilanka Ringwald 
426fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
427fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
428021192e1SMilanka Ringwald                 continue;
429021192e1SMilanka Ringwald             }
430fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
431fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
432fd39e93aSMilanka Ringwald                 continue;
433fd39e93aSMilanka Ringwald             }
434fd39e93aSMilanka Ringwald         }
435fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
436021192e1SMilanka Ringwald             index = i;
437021192e1SMilanka Ringwald         }
438021192e1SMilanka Ringwald     }
439021192e1SMilanka Ringwald     client->report_index = index;
440021192e1SMilanka Ringwald     return index;
441021192e1SMilanka Ringwald }
442021192e1SMilanka Ringwald 
443021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
444021192e1SMilanka Ringwald     client->report_index++;
445021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
446021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
447021192e1SMilanka Ringwald         return true;
448021192e1SMilanka Ringwald     }
449021192e1SMilanka Ringwald     return false;
450021192e1SMilanka Ringwald }
451021192e1SMilanka Ringwald 
452021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
453cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
454cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
455cd28e7b1SMilanka Ringwald #endif
456021192e1SMilanka Ringwald     client->report_index = 0;
457021192e1SMilanka Ringwald 
458021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
459021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
460021192e1SMilanka Ringwald         return true;
461021192e1SMilanka Ringwald     }
462021192e1SMilanka Ringwald     return false;
463021192e1SMilanka Ringwald }
464021192e1SMilanka Ringwald 
465cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
466cd28e7b1SMilanka Ringwald     client->report_index++;
467cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
468cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
469cd28e7b1SMilanka Ringwald         return true;
470cd28e7b1SMilanka Ringwald     }
471cd28e7b1SMilanka Ringwald     return false;
472cd28e7b1SMilanka Ringwald }
473cd28e7b1SMilanka Ringwald 
474cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
475cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
476cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
477cd28e7b1SMilanka Ringwald #endif
478cd28e7b1SMilanka Ringwald     client->report_index = 0;
479cd28e7b1SMilanka Ringwald 
480cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
481cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
482cd28e7b1SMilanka Ringwald         return true;
483cd28e7b1SMilanka Ringwald     }
484cd28e7b1SMilanka Ringwald     return false;
485cd28e7b1SMilanka Ringwald }
486cd28e7b1SMilanka Ringwald 
487cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
488cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
489cf26c8fbSMilanka Ringwald     if (!client){
490cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
491cf26c8fbSMilanka Ringwald         return NULL;
492cf26c8fbSMilanka Ringwald     }
493cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
494cf26c8fbSMilanka Ringwald     client->cid = cid;
495cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
496fc975d0eSMilanka Ringwald 
497cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
498cf26c8fbSMilanka Ringwald     return client;
499fc975d0eSMilanka Ringwald }
500fc975d0eSMilanka Ringwald 
501cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
502021192e1SMilanka Ringwald     // stop listening
503021192e1SMilanka Ringwald     uint8_t i;
504021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
505021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
506021192e1SMilanka Ringwald     }
507021192e1SMilanka Ringwald 
508da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
509cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
510cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
511fc975d0eSMilanka Ringwald }
512cf26c8fbSMilanka Ringwald 
513cf26c8fbSMilanka Ringwald 
514cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5156bcfb631SMilanka Ringwald     uint8_t event[8];
516cf26c8fbSMilanka Ringwald     int pos = 0;
517cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
518cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
519cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
520cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
521cf26c8fbSMilanka Ringwald     pos += 2;
522cf26c8fbSMilanka Ringwald     event[pos++] = status;
523fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
524cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
525cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
526cf26c8fbSMilanka Ringwald }
527cf26c8fbSMilanka Ringwald 
528cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
529cd28e7b1SMilanka Ringwald     uint8_t event[6];
530cd28e7b1SMilanka Ringwald     int pos = 0;
531cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
532cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
533cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
534cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
535cd28e7b1SMilanka Ringwald     pos += 2;
536cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
537cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
538cd28e7b1SMilanka Ringwald }
539cd28e7b1SMilanka Ringwald 
540021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
541021192e1SMilanka Ringwald     uint16_t pos = 0;
542021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
543021192e1SMilanka Ringwald     pos++;  // skip len
544021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
545021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
546021192e1SMilanka Ringwald     pos += 2;
547021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
548021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
549021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
550021192e1SMilanka Ringwald     pos += 2;
551021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
55283d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
55383d7ed1cSMilanka Ringwald 
554021192e1SMilanka Ringwald }
555021192e1SMilanka Ringwald 
556f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
557f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
558f4d3b82aSMilanka Ringwald 
559f4d3b82aSMilanka Ringwald     uint8_t event[11];
560f4d3b82aSMilanka Ringwald     int pos = 0;
561f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
562f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
563f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
564f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
565f4d3b82aSMilanka Ringwald     pos += 2;
566f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
567f4d3b82aSMilanka Ringwald 
568f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
569f4d3b82aSMilanka Ringwald     pos += 3;
570f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
571f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
572f4d3b82aSMilanka Ringwald 
573f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
574f4d3b82aSMilanka Ringwald }
575f4d3b82aSMilanka Ringwald 
576af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
577af2241c2SMilanka Ringwald     if (value_len != 1) return;
578af2241c2SMilanka Ringwald 
579af2241c2SMilanka Ringwald     uint8_t event[11];
580af2241c2SMilanka Ringwald     int pos = 0;
581af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
582af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
583af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
584af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
585af2241c2SMilanka Ringwald     pos += 2;
586af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
587af2241c2SMilanka Ringwald     event[pos++] = value[0];
588af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
589af2241c2SMilanka Ringwald }
590f4d3b82aSMilanka Ringwald 
59183d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
592021192e1SMilanka Ringwald     UNUSED(packet_type);
593021192e1SMilanka Ringwald     UNUSED(channel);
59483d7ed1cSMilanka Ringwald 
595021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
596021192e1SMilanka Ringwald 
597021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
59817229983SMatthias Ringwald     if (client == NULL) return;
599021192e1SMilanka Ringwald 
600021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
601021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
602021192e1SMilanka Ringwald         return;
603021192e1SMilanka Ringwald     }
604021192e1SMilanka Ringwald 
60583d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
606021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
60783d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
608021192e1SMilanka Ringwald }
609021192e1SMilanka Ringwald 
61083d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
61183d7ed1cSMilanka Ringwald     UNUSED(packet_type);
61283d7ed1cSMilanka Ringwald     UNUSED(channel);
61383d7ed1cSMilanka Ringwald 
61483d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
61583d7ed1cSMilanka Ringwald 
61683d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
61717229983SMatthias Ringwald     if (client == NULL) return;
61883d7ed1cSMilanka Ringwald 
61983d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
62083d7ed1cSMilanka Ringwald         return;
62183d7ed1cSMilanka Ringwald     }
62283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
62383d7ed1cSMilanka Ringwald 
62483d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
62583d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
62683d7ed1cSMilanka Ringwald         return;
62783d7ed1cSMilanka Ringwald     }
62883d7ed1cSMilanka Ringwald 
62983d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
63083d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
63183d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
63283d7ed1cSMilanka Ringwald }
633cf26c8fbSMilanka Ringwald 
634cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
635cf26c8fbSMilanka Ringwald     uint8_t att_status;
6366bcfb631SMilanka Ringwald     gatt_client_service_t service;
6376bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
638cf26c8fbSMilanka Ringwald 
639cf26c8fbSMilanka Ringwald     switch (client->state){
640cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
641556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
642556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
643556456ccSMilanka Ringwald #endif
644cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
64519146789SMilanka Ringwald 
64619146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
647cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
648cf26c8fbSMilanka Ringwald             UNUSED(att_status);
649cf26c8fbSMilanka Ringwald             break;
650cf26c8fbSMilanka Ringwald 
651cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
652556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
653556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
654556456ccSMilanka Ringwald #endif
6556bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6566bcfb631SMilanka Ringwald 
6576bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6586bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
65919146789SMilanka Ringwald 
66019146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6616bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6626bcfb631SMilanka Ringwald 
6636bcfb631SMilanka Ringwald             UNUSED(att_status);
6646bcfb631SMilanka Ringwald             break;
6656bcfb631SMilanka Ringwald 
66628da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
667556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
668556456ccSMilanka 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);
669556456ccSMilanka Ringwald #endif
67028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
671556456ccSMilanka Ringwald 
67219146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
67319146789SMilanka 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);
674e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
675e7bd2dbeSMilanka Ringwald             break;
676e7bd2dbeSMilanka Ringwald 
677e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
678556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
679556456ccSMilanka 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);
680556456ccSMilanka Ringwald #endif
681e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
682e7bd2dbeSMilanka Ringwald 
6833322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6843322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6853322b222SMilanka Ringwald 
68619146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
687e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
688e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
689e7bd2dbeSMilanka Ringwald             break;
690e7bd2dbeSMilanka Ringwald 
69128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
692556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
69319146789SMilanka 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);
694556456ccSMilanka Ringwald #endif
69528da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
696e7bd2dbeSMilanka Ringwald 
69719146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
69819146789SMilanka 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);
699e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
700e7bd2dbeSMilanka Ringwald             break;
701e7bd2dbeSMilanka Ringwald 
7023322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
703556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
70419146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
705556456ccSMilanka Ringwald #endif
7063322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
707e7bd2dbeSMilanka Ringwald 
7083322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7093322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
71019146789SMilanka Ringwald 
71119146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7123322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
713e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
714e7bd2dbeSMilanka Ringwald             break;
715e7bd2dbeSMilanka Ringwald 
71628da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
717556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
71883d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
719556456ccSMilanka Ringwald                 client->report_index,
72083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
72183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
722556456ccSMilanka Ringwald #endif
72328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
724af2241c2SMilanka Ringwald             client->handle = 0;
725556456ccSMilanka Ringwald 
726556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
727556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
728556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
729556456ccSMilanka Ringwald 
73019146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
73170093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
73270093cf5SMilanka Ringwald             UNUSED(att_status);
73370093cf5SMilanka Ringwald             break;
73470093cf5SMilanka Ringwald 
73528da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
73628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
73770093cf5SMilanka Ringwald 
73819146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
739af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
740af2241c2SMilanka Ringwald             client->handle = 0;
74170093cf5SMilanka Ringwald             UNUSED(att_status);
74270093cf5SMilanka Ringwald             break;
74370093cf5SMilanka Ringwald 
744cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
745cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
746cd28e7b1SMilanka Ringwald             if (client->value > 0){
747cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
748cd28e7b1SMilanka Ringwald             } else {
749cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
750cd28e7b1SMilanka Ringwald             }
751cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
752cd28e7b1SMilanka Ringwald                 client->report_index,
753cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
754cd28e7b1SMilanka Ringwald #endif
755cd28e7b1SMilanka Ringwald 
756cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
757cd28e7b1SMilanka Ringwald 
758cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
759cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
760cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
761cd28e7b1SMilanka Ringwald 
762cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
763cd28e7b1SMilanka Ringwald 
764cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
765cd28e7b1SMilanka Ringwald 
766cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
767cd28e7b1SMilanka Ringwald                 switch(client->value){
768cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
769cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
770cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
771cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
772cd28e7b1SMilanka Ringwald                         break;
773cd28e7b1SMilanka Ringwald                     default:
774cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
775cd28e7b1SMilanka Ringwald                         break;
776cd28e7b1SMilanka Ringwald                 }
777cd28e7b1SMilanka Ringwald             } else {
778cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
779cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
780cd28e7b1SMilanka Ringwald                     break;
781cd28e7b1SMilanka Ringwald                 }
782cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
783cd28e7b1SMilanka Ringwald             }
784cd28e7b1SMilanka Ringwald             break;
785cd28e7b1SMilanka Ringwald 
786021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
787cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
788cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
789cd28e7b1SMilanka Ringwald                 client->report_index,
790cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
791cd28e7b1SMilanka Ringwald #endif
792021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
793021192e1SMilanka Ringwald 
794021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
795021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
796021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
797021192e1SMilanka Ringwald 
79819146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
799021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
800021192e1SMilanka Ringwald 
801cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
802cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
803cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
804cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
805cd28e7b1SMilanka Ringwald             } else {
806021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
807021192e1SMilanka Ringwald                     hids_run_for_client(client);
808021192e1SMilanka Ringwald                     break;
809021192e1SMilanka Ringwald                 }
810021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
811021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
812021192e1SMilanka Ringwald             }
813021192e1SMilanka Ringwald             break;
814021192e1SMilanka Ringwald 
81583d7ed1cSMilanka Ringwald 
8166d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
81783d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8186d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
81983d7ed1cSMilanka Ringwald                 client->report_index,
82083d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
82183d7ed1cSMilanka Ringwald #endif
82283d7ed1cSMilanka Ringwald 
8236d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
82483d7ed1cSMilanka Ringwald 
8256d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8266d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
8276d6f7efcSMilanka Ringwald                 &handle_report_event, client->con_handle,
82883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
82983d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
83083d7ed1cSMilanka Ringwald             UNUSED(att_status);
83183d7ed1cSMilanka Ringwald             break;
83283d7ed1cSMilanka Ringwald 
83383d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
83483d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
835f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
836f4d3b82aSMilanka Ringwald                 client->report_index,
83783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
83883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
83983d7ed1cSMilanka Ringwald #endif
84083d7ed1cSMilanka Ringwald 
84183d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
842f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
84383d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
84483d7ed1cSMilanka Ringwald                 &handle_report_event,
84583d7ed1cSMilanka Ringwald                 client->con_handle,
84683d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
84783d7ed1cSMilanka Ringwald             UNUSED(att_status);
84883d7ed1cSMilanka Ringwald             break;
84983d7ed1cSMilanka Ringwald 
85083d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
85183d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
85283d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
85383d7ed1cSMilanka Ringwald 
854f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
85583d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
85683d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
85783d7ed1cSMilanka Ringwald                 client->con_handle,
85883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
85983d7ed1cSMilanka Ringwald 
86083d7ed1cSMilanka Ringwald             break;
86183d7ed1cSMilanka Ringwald #endif
862af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
863af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
86483d7ed1cSMilanka Ringwald 
865f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
866f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
867f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
868f4d3b82aSMilanka Ringwald                 client->con_handle,
869af2241c2SMilanka Ringwald                 client->handle);
870f4d3b82aSMilanka Ringwald             break;
8716d6f7efcSMilanka Ringwald 
872fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8736d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
8741fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
8751fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
8761fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
8771fa7278eSMatthias Ringwald             break;
8781fa7278eSMatthias Ringwald 
8791fa7278eSMatthias Ringwald         default:
8801fa7278eSMatthias Ringwald             break;
8811fa7278eSMatthias Ringwald     }
8821fa7278eSMatthias Ringwald }
8831fa7278eSMatthias Ringwald 
8841fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
8851fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
8861fa7278eSMatthias Ringwald     uint8_t att_status;
8871fa7278eSMatthias Ringwald     switch (client->state){
8881fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
8891fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
8901fa7278eSMatthias Ringwald                 client->con_handle,
8911fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
8921fa7278eSMatthias Ringwald 
8931fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
8941fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
8951fa7278eSMatthias Ringwald #endif
8961fa7278eSMatthias Ringwald 
8971fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
8981fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
8991fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
9001fa7278eSMatthias Ringwald                     client->service_index++;
9011fa7278eSMatthias Ringwald                     hids_run_for_client(client);
9021fa7278eSMatthias Ringwald                     break;
9031fa7278eSMatthias Ringwald                 }
9041fa7278eSMatthias Ringwald             }
9051fa7278eSMatthias Ringwald 
9061fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
9071fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
9081fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9091fa7278eSMatthias Ringwald                 break;
9101fa7278eSMatthias Ringwald             }
9111fa7278eSMatthias Ringwald 
9121fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
9131fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
9141fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
9151fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9161fa7278eSMatthias Ringwald                 break;
9171fa7278eSMatthias Ringwald             }
9181fa7278eSMatthias Ringwald 
9191fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9201fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9211fa7278eSMatthias Ringwald             break;
9221fa7278eSMatthias Ringwald 
9231fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9241fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9251fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9261fa7278eSMatthias Ringwald #endif
9271fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9281fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9296d6f7efcSMilanka Ringwald             break;
9306d6f7efcSMilanka Ringwald 
9316bcfb631SMilanka Ringwald         default:
9326bcfb631SMilanka Ringwald             break;
9336bcfb631SMilanka Ringwald     }
9346bcfb631SMilanka Ringwald }
9356bcfb631SMilanka Ringwald 
936cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
937cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
938cf26c8fbSMilanka Ringwald     UNUSED(channel);
939cf26c8fbSMilanka Ringwald     UNUSED(size);
940cf26c8fbSMilanka Ringwald 
9416bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
942cf26c8fbSMilanka Ringwald     uint8_t att_status;
9436bcfb631SMilanka Ringwald     gatt_client_service_t service;
9446bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
94570093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
946ab116b1cSMilanka Ringwald 
947021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
948021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
949e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9503322b222SMilanka Ringwald     uint8_t i;
9513322b222SMilanka Ringwald     uint8_t report_index;
952da142a6fSMilanka Ringwald 
953f4d3b82aSMilanka Ringwald     const uint8_t * value;
954f4d3b82aSMilanka Ringwald     uint16_t value_len;
955cf26c8fbSMilanka Ringwald 
956cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
957cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
958cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
95917229983SMatthias Ringwald             if (client == NULL) break;
960cf26c8fbSMilanka Ringwald 
961021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
962021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
9636bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
964021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
965021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
9666bcfb631SMilanka Ringwald                 client->num_instances++;
967021192e1SMilanka Ringwald 
968708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
969708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
970708c69d2SMilanka Ringwald #endif
971021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
972021192e1SMilanka Ringwald             }  else {
973021192e1SMilanka 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);
974da142a6fSMilanka Ringwald             }
9756bcfb631SMilanka Ringwald             break;
9766bcfb631SMilanka Ringwald 
9776bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
9786bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
97917229983SMatthias Ringwald             if (client == NULL) break;
98017229983SMatthias Ringwald 
9816bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
98219146789SMilanka Ringwald 
98319146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
98419146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
98519146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
98619146789SMilanka Ringwald                     break;
98719146789SMilanka Ringwald                 }
98819146789SMilanka Ringwald             }
98919146789SMilanka Ringwald 
99019146789SMilanka Ringwald             switch (characteristic.uuid16){
99119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
992af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
99319146789SMilanka Ringwald                     break;
99419146789SMilanka Ringwald 
99519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
99619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
99719146789SMilanka Ringwald                     break;
99819146789SMilanka Ringwald 
99919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
100019146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
100119146789SMilanka Ringwald                     break;
100219146789SMilanka Ringwald 
100319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1004f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
100519146789SMilanka Ringwald                     break;
100619146789SMilanka Ringwald 
100719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
100819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
100919146789SMilanka Ringwald                     break;
101019146789SMilanka Ringwald 
101119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
101219146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
101319146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
101419146789SMilanka Ringwald                     break;
101519146789SMilanka Ringwald 
101619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1017f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
101819146789SMilanka Ringwald                     break;
101919146789SMilanka Ringwald 
102019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1021f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
102219146789SMilanka Ringwald                     break;
102319146789SMilanka Ringwald 
102419146789SMilanka Ringwald                 default:
102519146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
102619146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
102719146789SMilanka Ringwald #endif
102819146789SMilanka Ringwald                     return;
102919146789SMilanka Ringwald             }
103019146789SMilanka Ringwald 
103119146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103219146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
103319146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
103419146789SMilanka Ringwald                 characteristic.start_handle,
103519146789SMilanka Ringwald                 characteristic.properties,
103619146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
103719146789SMilanka Ringwald                 client->service_index);
103819146789SMilanka Ringwald 
103919146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
104019146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
104119146789SMilanka Ringwald             }
104219146789SMilanka Ringwald             printf("\n");
104319146789SMilanka Ringwald #endif
1044cf26c8fbSMilanka Ringwald             break;
1045cf26c8fbSMilanka Ringwald 
10468cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1047e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10488cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
104917229983SMatthias Ringwald             if (client == NULL) break;
1050da142a6fSMilanka Ringwald 
1051f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1052f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10538cec2b74SMilanka Ringwald 
1054556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10558cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1056f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1057556456ccSMilanka Ringwald #endif
1058f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1059f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1060da142a6fSMilanka Ringwald                 if (!stored){
1061da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1062da142a6fSMilanka Ringwald                     break;
1063da142a6fSMilanka Ringwald                 }
1064da142a6fSMilanka Ringwald             }
1065e7bd2dbeSMilanka Ringwald             break;
1066e7bd2dbeSMilanka Ringwald 
106770093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
106870093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
106917229983SMatthias Ringwald             if (client == NULL) break;
107017229983SMatthias Ringwald 
107170093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
107270093cf5SMilanka Ringwald 
1073e7bd2dbeSMilanka Ringwald             switch (client->state) {
1074e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1075e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1076e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
107719146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
10783322b222SMilanka Ringwald 
1079556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1080556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1081556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1082556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1083556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1084556456ccSMilanka Ringwald                                 client->service_index, report_index);
1085556456ccSMilanka Ringwald                         }
1086556456ccSMilanka Ringwald #endif
1087e7bd2dbeSMilanka Ringwald                     }
1088e7bd2dbeSMilanka Ringwald                     break;
108928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
109070093cf5SMilanka Ringwald                     // setup for descriptor value query
109170093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1092af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1093556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1094556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1095556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1096556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
109719146789SMilanka Ringwald #endif
1098556456ccSMilanka Ringwald                     }
1099556456ccSMilanka Ringwald 
110019146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1101556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
110283d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1103556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1104556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1105556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1106556456ccSMilanka Ringwald                     }
1107556456ccSMilanka Ringwald #endif
110870093cf5SMilanka Ringwald                     break;
1109556456ccSMilanka Ringwald 
1110e7bd2dbeSMilanka Ringwald                 default:
1111e7bd2dbeSMilanka Ringwald                     break;
1112e7bd2dbeSMilanka Ringwald             }
1113e7bd2dbeSMilanka Ringwald             break;
111470093cf5SMilanka Ringwald 
111583d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
111683d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
111717229983SMatthias Ringwald             if (client == NULL) break;
111883d7ed1cSMilanka Ringwald 
1119f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1120f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1121f4d3b82aSMilanka Ringwald 
1122af2241c2SMilanka Ringwald 
1123f4d3b82aSMilanka Ringwald             switch (client->state){
1124f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1125f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
112683d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1127f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
112883d7ed1cSMilanka Ringwald                     break;
112983d7ed1cSMilanka Ringwald #endif
1130af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1131af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1132af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1133f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1134f4d3b82aSMilanka Ringwald                         break;
1135af2241c2SMilanka Ringwald                     }
1136af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1137af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1138af2241c2SMilanka Ringwald                         break;
1139af2241c2SMilanka Ringwald                     }
1140af2241c2SMilanka Ringwald                     break;
1141af2241c2SMilanka Ringwald                 }
1142f4d3b82aSMilanka Ringwald                 default:
1143f4d3b82aSMilanka Ringwald                     break;
1144f4d3b82aSMilanka Ringwald             }
1145f4d3b82aSMilanka Ringwald 
1146f4d3b82aSMilanka Ringwald             break;
114783d7ed1cSMilanka Ringwald 
114870093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
114970093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
115017229983SMatthias Ringwald             if (client == NULL) break;
1151e7bd2dbeSMilanka Ringwald 
1152e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1153e7bd2dbeSMilanka Ringwald                 break;
1154e7bd2dbeSMilanka Ringwald             }
11557e1e6e7dSMilanka Ringwald 
1156e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1157e7bd2dbeSMilanka Ringwald             switch (client->state) {
115828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1159e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
116019146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
11613322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
116219146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1163556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
116419146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
116519146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1166556456ccSMilanka Ringwald #endif
11673322b222SMilanka Ringwald                     }
1168e7bd2dbeSMilanka Ringwald                     break;
1169e7bd2dbeSMilanka Ringwald 
117028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
117119146789SMilanka Ringwald 
117219146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1173021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1174021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
117519146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
117683d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1177021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1178021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
117983d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
118019146789SMilanka Ringwald     #endif
118119146789SMilanka Ringwald                     }
1182e7bd2dbeSMilanka Ringwald                     break;
1183e7bd2dbeSMilanka Ringwald 
1184e7bd2dbeSMilanka Ringwald                 default:
1185e7bd2dbeSMilanka Ringwald                     break;
118670093cf5SMilanka Ringwald             }
118770093cf5SMilanka Ringwald             break;
118870093cf5SMilanka Ringwald 
1189cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1190cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
119117229983SMatthias Ringwald             if (client == NULL) break;
1192cf26c8fbSMilanka Ringwald 
1193cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1194cf26c8fbSMilanka Ringwald 
1195cf26c8fbSMilanka Ringwald             switch (client->state){
1196cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
11976bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
11986bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
11996bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12005fa2c39aSMilanka Ringwald                         return;
12016bcfb631SMilanka Ringwald                     }
12026bcfb631SMilanka Ringwald 
12036bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12046bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12056bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12065fa2c39aSMilanka Ringwald                         return;
12076bcfb631SMilanka Ringwald                     }
12086bcfb631SMilanka Ringwald 
12096bcfb631SMilanka Ringwald                     client->service_index = 0;
121019146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12116bcfb631SMilanka Ringwald                     break;
12126bcfb631SMilanka Ringwald 
12136bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
12146bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
12156bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
12166bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12175fa2c39aSMilanka Ringwald                         return;
12186bcfb631SMilanka Ringwald                     }
12196bcfb631SMilanka Ringwald 
122070093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
122170093cf5SMilanka Ringwald                         // discover characteristics of next service
122270093cf5SMilanka Ringwald                         client->service_index++;
122370093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1224021192e1SMilanka Ringwald                         break;
1225021192e1SMilanka Ringwald                     }
1226021192e1SMilanka Ringwald 
1227fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1228fd39e93aSMilanka Ringwald 
1229021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1230021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1231fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1232fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1233021192e1SMilanka Ringwald                             }
12347e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12357e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12367e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
123770093cf5SMilanka Ringwald                                 break;
123870093cf5SMilanka Ringwald                             }
123970093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
124070093cf5SMilanka Ringwald                             hids_finalize_client(client);
1241fd39e93aSMilanka Ringwald                             return;
1242fd39e93aSMilanka Ringwald 
1243fd39e93aSMilanka Ringwald                         default:
1244fd39e93aSMilanka Ringwald                             // set boot mode
1245fd39e93aSMilanka Ringwald                             client->service_index = 0;
1246fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1247fd39e93aSMilanka Ringwald                             break;
1248fd39e93aSMilanka Ringwald                     }
12496bcfb631SMilanka Ringwald                     break;
12506bcfb631SMilanka Ringwald 
12513322b222SMilanka Ringwald 
125228da36a6SMilanka Ringwald                 // HID descriptor found
125328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1254e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1255e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1256e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12575fa2c39aSMilanka Ringwald                         return;
1258e7bd2dbeSMilanka Ringwald                     }
1259e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1260e7bd2dbeSMilanka Ringwald                     break;
1261e7bd2dbeSMilanka Ringwald 
126228da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1263e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
12643322b222SMilanka Ringwald                     // go for next report map
12653322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1266e7bd2dbeSMilanka Ringwald                         break;
1267e7bd2dbeSMilanka Ringwald                     }
1268e7bd2dbeSMilanka Ringwald 
12693322b222SMilanka Ringwald                     // read UUIDS for external characteristics
12703322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1271e7bd2dbeSMilanka Ringwald                         break;
1272e7bd2dbeSMilanka Ringwald                     }
1273e7bd2dbeSMilanka Ringwald 
1274e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1275e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12767e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1277e7bd2dbeSMilanka Ringwald                         break;
1278e7bd2dbeSMilanka Ringwald                     }
1279e7bd2dbeSMilanka Ringwald 
1280e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1281e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
12825fa2c39aSMilanka Ringwald                     return;
1283e7bd2dbeSMilanka Ringwald 
128428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1285e7bd2dbeSMilanka Ringwald                     // go for next map report
12863322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1287e7bd2dbeSMilanka Ringwald                         break;
1288e7bd2dbeSMilanka Ringwald                     }
1289e7bd2dbeSMilanka Ringwald 
12903322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
12913322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1292e7bd2dbeSMilanka Ringwald                     break;
1293e7bd2dbeSMilanka Ringwald 
12943322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1295e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1296e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
12977e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1298e7bd2dbeSMilanka Ringwald                         break;
1299e7bd2dbeSMilanka Ringwald                     }
1300e7bd2dbeSMilanka Ringwald 
1301e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1302e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13035fa2c39aSMilanka Ringwald                     return;
1304e7bd2dbeSMilanka Ringwald 
130528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1306af2241c2SMilanka Ringwald                     if (client->handle != 0){
130728da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
130870093cf5SMilanka Ringwald                         break;
130970093cf5SMilanka Ringwald                     }
131070093cf5SMilanka Ringwald                     // go for next report
13117e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
131270093cf5SMilanka Ringwald                         break;
131370093cf5SMilanka Ringwald                     }
1314835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
131570093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
131670093cf5SMilanka Ringwald                     break;
131770093cf5SMilanka Ringwald 
131828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
131970093cf5SMilanka Ringwald                     // go for next report
13207e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
132170093cf5SMilanka Ringwald                         break;
132270093cf5SMilanka Ringwald                     }
1323021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1324021192e1SMilanka Ringwald                         break;
1325021192e1SMilanka Ringwald                     }
1326835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
132770093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
132870093cf5SMilanka Ringwald                     break;
132970093cf5SMilanka Ringwald 
1330021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1331021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13322901a9b7SMilanka Ringwald                         break;
13332901a9b7SMilanka Ringwald                     }
1334835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13356bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13366bcfb631SMilanka Ringwald                     break;
1337f4d3b82aSMilanka Ringwald 
1338cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1339cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1340cd28e7b1SMilanka Ringwald                         break;
1341cd28e7b1SMilanka Ringwald                     }
1342cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1343cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1344cd28e7b1SMilanka Ringwald                     break;
1345cd28e7b1SMilanka Ringwald 
1346835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1347835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1348835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1349835a13f1SMilanka Ringwald                     break;
1350835a13f1SMilanka Ringwald #endif
1351f4d3b82aSMilanka Ringwald 
1352af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
13536d6f7efcSMilanka Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1354f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1355f4d3b82aSMilanka Ringwald                     break;
1356f4d3b82aSMilanka Ringwald 
13576d6f7efcSMilanka Ringwald 
1358cf26c8fbSMilanka Ringwald                 default:
1359cf26c8fbSMilanka Ringwald                     break;
1360cf26c8fbSMilanka Ringwald             }
1361cf26c8fbSMilanka Ringwald             break;
1362cf26c8fbSMilanka Ringwald 
1363cf26c8fbSMilanka Ringwald         default:
1364cf26c8fbSMilanka Ringwald             break;
1365cf26c8fbSMilanka Ringwald     }
13666bcfb631SMilanka Ringwald 
13676bcfb631SMilanka Ringwald     if (client != NULL){
13686bcfb631SMilanka Ringwald         hids_run_for_client(client);
13696bcfb631SMilanka Ringwald     }
1370cf26c8fbSMilanka Ringwald }
1371cf26c8fbSMilanka Ringwald 
13726bcfb631SMilanka 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){
1373cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1374cf26c8fbSMilanka Ringwald 
1375cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1376cf26c8fbSMilanka Ringwald     if (client != NULL){
1377cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1378cf26c8fbSMilanka Ringwald     }
1379cf26c8fbSMilanka Ringwald 
1380cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1381cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1382cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1383cf26c8fbSMilanka Ringwald     }
1384cf26c8fbSMilanka Ringwald 
1385cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1386cf26c8fbSMilanka Ringwald     if (client == NULL) {
1387cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1388cf26c8fbSMilanka Ringwald     }
1389cf26c8fbSMilanka Ringwald 
13906bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1391cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1392cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1393cf26c8fbSMilanka Ringwald 
1394cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1395cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1396cf26c8fbSMilanka Ringwald }
1397cf26c8fbSMilanka Ringwald 
1398cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1399cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1400cf26c8fbSMilanka Ringwald     if (client == NULL){
1401cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1402cf26c8fbSMilanka Ringwald     }
1403cf26c8fbSMilanka Ringwald     // finalize connection
1404cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1405cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1406cf26c8fbSMilanka Ringwald }
1407cf26c8fbSMilanka Ringwald 
1408fd39e93aSMilanka Ringwald uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type, const uint8_t * report, uint8_t report_len){
14091624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14101624214bSMilanka Ringwald     if (client == NULL){
14111624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14121624214bSMilanka Ringwald     }
14131624214bSMilanka Ringwald 
14141624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14151624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14161624214bSMilanka Ringwald     }
14171624214bSMilanka Ringwald 
1418fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
141983d7ed1cSMilanka Ringwald 
14201624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14211624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14221624214bSMilanka Ringwald     }
14231624214bSMilanka Ringwald 
14241624214bSMilanka Ringwald     uint16_t mtu;
14251624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
14261624214bSMilanka Ringwald 
14271624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
14281624214bSMilanka Ringwald         return status;
14291624214bSMilanka Ringwald     }
14301624214bSMilanka Ringwald 
14311624214bSMilanka Ringwald     if (mtu - 2 < report_len){
14321624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
14331624214bSMilanka Ringwald     }
14341624214bSMilanka Ringwald 
14356d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1436021192e1SMilanka Ringwald     client->report_index = report_index;
14371624214bSMilanka Ringwald     client->report = report;
14381624214bSMilanka Ringwald     client->report_len = report_len;
14391624214bSMilanka Ringwald 
14401624214bSMilanka Ringwald     hids_run_for_client(client);
14411624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
14421624214bSMilanka Ringwald }
14431624214bSMilanka Ringwald 
1444fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
144583d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
144683d7ed1cSMilanka Ringwald     if (client == NULL){
144783d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
144883d7ed1cSMilanka Ringwald     }
144983d7ed1cSMilanka Ringwald 
145083d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
145183d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
145283d7ed1cSMilanka Ringwald     }
145383d7ed1cSMilanka Ringwald 
1454fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
145583d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
145683d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
145783d7ed1cSMilanka Ringwald     }
145883d7ed1cSMilanka Ringwald 
145983d7ed1cSMilanka Ringwald     client->report_index = report_index;
146083d7ed1cSMilanka Ringwald 
146183d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
146283d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
146383d7ed1cSMilanka Ringwald #else
146483d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
146583d7ed1cSMilanka Ringwald #endif
146683d7ed1cSMilanka Ringwald     hids_run_for_client(client);
146783d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
146883d7ed1cSMilanka Ringwald }
146983d7ed1cSMilanka Ringwald 
147083d7ed1cSMilanka Ringwald 
1471f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1472f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1473f4d3b82aSMilanka Ringwald     if (client == NULL){
1474f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1475f4d3b82aSMilanka Ringwald     }
1476f4d3b82aSMilanka Ringwald 
1477f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1478f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1479f4d3b82aSMilanka Ringwald     }
1480f4d3b82aSMilanka Ringwald 
1481f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1482f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1483f4d3b82aSMilanka Ringwald     }
1484f4d3b82aSMilanka Ringwald 
1485f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1486af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1487af2241c2SMilanka Ringwald 
1488af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1489af2241c2SMilanka Ringwald     hids_run_for_client(client);
1490af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1491af2241c2SMilanka Ringwald }
1492af2241c2SMilanka Ringwald 
1493af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1494af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1495af2241c2SMilanka Ringwald     if (client == NULL){
1496af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1497af2241c2SMilanka Ringwald     }
1498af2241c2SMilanka Ringwald 
1499af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1500af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1501af2241c2SMilanka Ringwald     }
1502af2241c2SMilanka Ringwald 
1503af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1504af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1505af2241c2SMilanka Ringwald     }
1506af2241c2SMilanka Ringwald 
1507af2241c2SMilanka Ringwald     client->service_index = service_index;
1508af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1509af2241c2SMilanka Ringwald 
1510af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1511f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1512f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1513f4d3b82aSMilanka Ringwald }
1514f4d3b82aSMilanka Ringwald 
15150cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15166d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15176d6f7efcSMilanka Ringwald     if (client == NULL){
15186d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15196d6f7efcSMilanka Ringwald     }
15206d6f7efcSMilanka Ringwald 
15216d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15226d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15236d6f7efcSMilanka Ringwald     }
15246d6f7efcSMilanka Ringwald 
15256d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15266d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15276d6f7efcSMilanka Ringwald     }
15286d6f7efcSMilanka Ringwald 
15296d6f7efcSMilanka Ringwald     client->service_index = service_index;
15306d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
15316d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
15326d6f7efcSMilanka Ringwald 
15336d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15346d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15356d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15366d6f7efcSMilanka Ringwald }
15376d6f7efcSMilanka Ringwald 
15386d6f7efcSMilanka Ringwald 
15396d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
15406d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15416d6f7efcSMilanka Ringwald     if (client == NULL){
15426d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15436d6f7efcSMilanka Ringwald     }
15446d6f7efcSMilanka Ringwald 
15456d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15466d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
15476d6f7efcSMilanka Ringwald     }
15486d6f7efcSMilanka Ringwald 
15496d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
15506d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
15516d6f7efcSMilanka Ringwald     }
15526d6f7efcSMilanka Ringwald 
15536d6f7efcSMilanka Ringwald     client->service_index = service_index;
15546d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
15556d6f7efcSMilanka Ringwald     client->value = value;
15566d6f7efcSMilanka Ringwald 
15576d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
15586d6f7efcSMilanka Ringwald     hids_run_for_client(client);
15596d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15606d6f7efcSMilanka Ringwald }
15616d6f7efcSMilanka Ringwald 
15626d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
15636d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
15646d6f7efcSMilanka Ringwald }
15656d6f7efcSMilanka Ringwald 
15666d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
15676d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
15686d6f7efcSMilanka Ringwald }
15696d6f7efcSMilanka Ringwald 
15700cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1571cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1572cd28e7b1SMilanka Ringwald     if (client == NULL){
1573cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1574cd28e7b1SMilanka Ringwald     }
1575cd28e7b1SMilanka Ringwald 
1576cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1577cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1578cd28e7b1SMilanka Ringwald     }
1579cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1580cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1581cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1582cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1583cd28e7b1SMilanka Ringwald     }
1584cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1585cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1586cd28e7b1SMilanka Ringwald }
1587cd28e7b1SMilanka Ringwald 
15880cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1589cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1590cd28e7b1SMilanka Ringwald     if (client == NULL){
1591cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1592cd28e7b1SMilanka Ringwald     }
1593cd28e7b1SMilanka Ringwald 
1594cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1595cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1596cd28e7b1SMilanka Ringwald     }
1597cd28e7b1SMilanka Ringwald 
1598cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1599cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1600cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1601cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1602cd28e7b1SMilanka Ringwald     }
1603cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1604cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1605cd28e7b1SMilanka Ringwald }
16066d6f7efcSMilanka Ringwald 
1607021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1608021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1609021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1610021192e1SMilanka Ringwald }
1611cf26c8fbSMilanka Ringwald 
1612cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1613