xref: /btstack/src/ble/gatt-service/hids_client.c (revision 61776ecd4af57608daff473952738d9670d262ca)
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"
59b60c1680SMatthias Ringwald #include "ble/gatt_service_client.h"
60fc975d0eSMilanka Ringwald 
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6570093cf5SMilanka Ringwald 
66e5451bcdSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
67e5451bcdSMatthias Ringwald 
68da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
69da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
70da142a6fSMilanka Ringwald 
71da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
72da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
73da142a6fSMilanka Ringwald 
74cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
751fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context);
76cf26c8fbSMilanka Ringwald 
77556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
hid_characteristic_name(uint16_t uuid)78556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
79556456ccSMilanka Ringwald     switch (uuid){
80556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
81556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
82556456ccSMilanka Ringwald 
83556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
84556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
85556456ccSMilanka Ringwald 
86556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
87556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
88556456ccSMilanka Ringwald 
89556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
90556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
91556456ccSMilanka Ringwald 
92556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
93556456ccSMilanka Ringwald             return "REPORT";
94556456ccSMilanka Ringwald 
95556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
96556456ccSMilanka Ringwald             return "REPORT_MAP";
97556456ccSMilanka Ringwald 
98556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
99556456ccSMilanka Ringwald             return "HID_INFORMATION";
100556456ccSMilanka Ringwald 
101556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
102556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
103556456ccSMilanka Ringwald         default:
104556456ccSMilanka Ringwald             return "UKNOWN";
105556456ccSMilanka Ringwald     }
106556456ccSMilanka Ringwald }
107556456ccSMilanka Ringwald #endif
108556456ccSMilanka Ringwald 
hids_get_client_for_con_handle(hci_con_handle_t con_handle)109da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
110da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
111da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
112da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
113da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
114da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
115da142a6fSMilanka Ringwald         return client;
116da142a6fSMilanka Ringwald     }
117da142a6fSMilanka Ringwald     return NULL;
118da142a6fSMilanka Ringwald }
119da142a6fSMilanka Ringwald 
hids_get_client_for_cid(uint16_t hids_cid)120da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
121da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
122da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
123da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
124da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
125da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
126da142a6fSMilanka Ringwald         return client;
127da142a6fSMilanka Ringwald     }
128da142a6fSMilanka Ringwald     return NULL;
129da142a6fSMilanka Ringwald }
130da142a6fSMilanka Ringwald 
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald // START Descriptor Storage Util
133da142a6fSMilanka Ringwald 
hids_client_descriptors_len(hids_client_t * client)134a391be9aSMatthias Ringwald static uint16_t hids_client_descriptors_len(hids_client_t * client){
135a391be9aSMatthias Ringwald     uint16_t descriptors_len = 0;
136a391be9aSMatthias Ringwald     uint8_t service_index;
137a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
138a391be9aSMatthias Ringwald         descriptors_len += client->services[service_index].hid_descriptor_len;
139a391be9aSMatthias Ringwald     }
140a391be9aSMatthias Ringwald     return descriptors_len;
141a391be9aSMatthias Ringwald }
142a391be9aSMatthias Ringwald 
hids_client_descriptor_storage_get_available_space(void)143da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
144da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
145da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
146da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
147da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
148da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
149da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
150a391be9aSMatthias Ringwald         free_space -= hids_client_descriptors_len(client);
151da142a6fSMilanka Ringwald     }
152da142a6fSMilanka Ringwald     return free_space;
153da142a6fSMilanka Ringwald }
154da142a6fSMilanka Ringwald 
hids_client_descriptor_storage_init(hids_client_t * client,uint8_t service_index)155da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
156a391be9aSMatthias Ringwald     // reserve remaining space for this connection
157a391be9aSMatthias Ringwald     uint16_t available_space = hids_client_descriptor_storage_get_available_space();
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
159a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_max_len = available_space;
160a391be9aSMatthias Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
161da142a6fSMilanka Ringwald }
162da142a6fSMilanka Ringwald 
hids_client_descriptor_storage_store(hids_client_t * client,uint8_t service_index,uint8_t byte)163da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
164a391be9aSMatthias Ringwald     // store single hid descriptor byte
165da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
166da142a6fSMilanka Ringwald 
167da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
168da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
169da142a6fSMilanka Ringwald     return true;
170da142a6fSMilanka Ringwald }
171da142a6fSMilanka Ringwald 
hids_client_descriptor_storage_delete(hids_client_t * client)172da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
173a391be9aSMatthias Ringwald     uint8_t service_index;
174da142a6fSMilanka Ringwald 
175a391be9aSMatthias Ringwald     // calculate descriptors len
176a391be9aSMatthias Ringwald     uint16_t descriptors_len = hids_client_descriptors_len(client);
177da142a6fSMilanka Ringwald 
178a391be9aSMatthias Ringwald     if (descriptors_len > 0){
179a391be9aSMatthias Ringwald         // move higher descriptors down
180a391be9aSMatthias Ringwald         uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
181da142a6fSMilanka Ringwald         memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
182da142a6fSMilanka Ringwald                 &hids_client_descriptor_storage[next_offset],
183da142a6fSMilanka Ringwald                 hids_client_descriptor_storage_len - next_offset);
184da142a6fSMilanka Ringwald 
185a391be9aSMatthias Ringwald         // fix descriptor offset of higher descriptors
186da142a6fSMilanka Ringwald         btstack_linked_list_iterator_t it;
187da142a6fSMilanka Ringwald         btstack_linked_list_iterator_init(&it, &clients);
188da142a6fSMilanka Ringwald         while (btstack_linked_list_iterator_has_next(&it)){
189da142a6fSMilanka Ringwald             hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
190a391be9aSMatthias Ringwald             if (conn == client) continue;
191a391be9aSMatthias Ringwald             for (service_index = 0; service_index < client->num_instances; service_index++){
192a391be9aSMatthias Ringwald                 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
193a391be9aSMatthias Ringwald                     conn->services[service_index].hid_descriptor_offset -= descriptors_len;
194da142a6fSMilanka Ringwald                 }
195da142a6fSMilanka Ringwald             }
196da142a6fSMilanka Ringwald         }
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald 
199a391be9aSMatthias Ringwald     // clear descriptors
200a391be9aSMatthias Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
201a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_len = 0;
202a391be9aSMatthias Ringwald         client->services[service_index].hid_descriptor_offset = 0;
203a391be9aSMatthias Ringwald     }
204a391be9aSMatthias Ringwald }
205a391be9aSMatthias Ringwald 
hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid,uint8_t service_index)206da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
207da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
208021192e1SMilanka Ringwald     if (client == NULL){
209021192e1SMilanka Ringwald         return NULL;
210021192e1SMilanka Ringwald     }
211021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
212da142a6fSMilanka Ringwald         return NULL;
213da142a6fSMilanka Ringwald     }
214da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
215da142a6fSMilanka Ringwald }
216da142a6fSMilanka Ringwald 
hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid,uint8_t service_index)217da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
218da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
219021192e1SMilanka Ringwald     if (client == NULL){
220021192e1SMilanka Ringwald         return 0;
221021192e1SMilanka Ringwald     }
222021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
223da142a6fSMilanka Ringwald         return 0;
224da142a6fSMilanka Ringwald     }
225da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
226da142a6fSMilanka Ringwald }
227da142a6fSMilanka Ringwald 
228da142a6fSMilanka Ringwald // END Descriptor Storage Util
229da142a6fSMilanka Ringwald 
hids_get_next_cid(void)230cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
231cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
232cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
233cf26c8fbSMilanka Ringwald     } else {
234cf26c8fbSMilanka Ringwald         hids_cid_counter++;
235cf26c8fbSMilanka Ringwald     }
236cf26c8fbSMilanka Ringwald     return hids_cid_counter;
237fc975d0eSMilanka Ringwald }
238fc975d0eSMilanka Ringwald 
find_report_index_for_value_handle(hids_client_t * client,uint16_t value_handle)239ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
240ab116b1cSMilanka Ringwald     uint8_t i;
241a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
242ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
243ab116b1cSMilanka Ringwald             return i;
244ab116b1cSMilanka Ringwald         }
245ab116b1cSMilanka Ringwald     }
246ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
247ab116b1cSMilanka Ringwald }
248ab116b1cSMilanka Ringwald 
find_external_report_index_for_value_handle(hids_client_t * client,uint16_t value_handle)24919146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
25019146789SMilanka Ringwald     uint8_t i;
25119146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
25219146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
25319146789SMilanka Ringwald             return i;
25419146789SMilanka Ringwald         }
25519146789SMilanka Ringwald     }
25619146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
25719146789SMilanka Ringwald }
25819146789SMilanka Ringwald 
external_report_index_for_uuid_exists(hids_client_t * client,uint16_t uuid16)25919146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
26019146789SMilanka Ringwald     uint8_t i;
26119146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
26219146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
26319146789SMilanka Ringwald             return true;
26419146789SMilanka Ringwald         }
26519146789SMilanka Ringwald     }
26619146789SMilanka Ringwald     return false;
26719146789SMilanka Ringwald }
26819146789SMilanka Ringwald 
find_report_index_for_report_id_and_report_type(hids_client_t * client,uint8_t report_id,hid_report_type_t report_type)269fd39e93aSMilanka 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){
27083d7ed1cSMilanka Ringwald     uint8_t i;
271835a13f1SMilanka Ringwald 
27283d7ed1cSMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
273fd39e93aSMilanka Ringwald         hids_client_report_t report = client->reports[i];
274fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
275fd39e93aSMilanka Ringwald 
276fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
27783d7ed1cSMilanka Ringwald             if (!client->reports[i].boot_report){
27883d7ed1cSMilanka Ringwald                 continue;
27983d7ed1cSMilanka Ringwald             }
280fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
28183d7ed1cSMilanka Ringwald             if (client->reports[i].boot_report){
28283d7ed1cSMilanka Ringwald                 continue;
28383d7ed1cSMilanka Ringwald             }
284fd39e93aSMilanka Ringwald         }
285fd39e93aSMilanka Ringwald 
286fd39e93aSMilanka Ringwald         if (report.report_id == report_id && report.report_type == report_type){
28783d7ed1cSMilanka Ringwald             return i;
28883d7ed1cSMilanka Ringwald         }
28983d7ed1cSMilanka Ringwald     }
29083d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
29183d7ed1cSMilanka Ringwald }
29219146789SMilanka Ringwald 
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)293021192e1SMilanka 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){
294ab116b1cSMilanka Ringwald 
29519146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
296ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2973322b222SMilanka Ringwald         return report_index;
298ab116b1cSMilanka Ringwald     }
29928da36a6SMilanka Ringwald     report_index = client->num_reports;
300ab116b1cSMilanka Ringwald 
30128da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
30228da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
30328da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
30428da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
305ab116b1cSMilanka Ringwald 
30628da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
30728da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
30828da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
309021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
31070093cf5SMilanka Ringwald 
311021192e1SMilanka 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);
312ab116b1cSMilanka Ringwald         client->num_reports++;
3133322b222SMilanka Ringwald         return report_index;
314ab116b1cSMilanka Ringwald     } else {
315ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3163322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
317ab116b1cSMilanka Ringwald     }
318ab116b1cSMilanka Ringwald }
319ab116b1cSMilanka Ringwald 
hids_client_add_external_report(hids_client_t * client,gatt_client_characteristic_descriptor_t * characteristic_descriptor)32019146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
321556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
322556456ccSMilanka Ringwald 
323556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
32419146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
325556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
32619146789SMilanka Ringwald 
327556456ccSMilanka Ringwald         client->num_external_reports++;
32819146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
329556456ccSMilanka Ringwald         return report_index;
330556456ccSMilanka Ringwald     } else {
331556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
332556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
33370093cf5SMilanka Ringwald     }
334556456ccSMilanka Ringwald }
335556456ccSMilanka Ringwald 
hids_client_get_next_active_report_map_index(hids_client_t * client)3367e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3377e1e6e7dSMilanka Ringwald     uint8_t i;
3383322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3393322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
340556456ccSMilanka Ringwald             return i;
3413322b222SMilanka Ringwald         }
3423322b222SMilanka Ringwald     }
343556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
344556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3453322b222SMilanka Ringwald }
3463322b222SMilanka Ringwald 
hids_client_report_query_next_report_map(hids_client_t * client)3473322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3483322b222SMilanka Ringwald     client->service_index++;
3493322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
350556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3513322b222SMilanka Ringwald         return true;
3523322b222SMilanka Ringwald     }
3533322b222SMilanka Ringwald     return false;
3543322b222SMilanka Ringwald }
3553322b222SMilanka Ringwald 
hids_client_report_map_query_init(hids_client_t * client)3563322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3573322b222SMilanka Ringwald     client->service_index = 0;
3583322b222SMilanka Ringwald 
3593322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3603322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3613322b222SMilanka Ringwald         return true;
3623322b222SMilanka Ringwald     }
3633322b222SMilanka Ringwald     return false;
3643322b222SMilanka Ringwald }
3653322b222SMilanka Ringwald 
hids_client_report_query_next_report_map_uuid(hids_client_t * client)3663322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
367021192e1SMilanka Ringwald     client->report_index++;
36819146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3693322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
370e7bd2dbeSMilanka Ringwald         return true;
371e7bd2dbeSMilanka Ringwald     }
372e7bd2dbeSMilanka Ringwald     return false;
373e7bd2dbeSMilanka Ringwald }
374e7bd2dbeSMilanka Ringwald 
hids_client_report_map_uuid_query_init(hids_client_t * client)3753322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
376021192e1SMilanka Ringwald     client->report_index = 0;
37719146789SMilanka Ringwald     if (client->num_external_reports > 0){
3783322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3797e1e6e7dSMilanka Ringwald         return true;
3807e1e6e7dSMilanka Ringwald     }
3817e1e6e7dSMilanka Ringwald     return false;
3827e1e6e7dSMilanka Ringwald }
3837e1e6e7dSMilanka Ringwald 
hids_client_get_next_report_index(hids_client_t * client)3847e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3857e1e6e7dSMilanka Ringwald     uint8_t i;
3867e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
387fd39e93aSMilanka Ringwald 
388021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
38970093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
390fd39e93aSMilanka Ringwald         if (!report.boot_report){
39170093cf5SMilanka Ringwald             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3927e1e6e7dSMilanka Ringwald                 index = i;
393021192e1SMilanka Ringwald                 client->service_index = report.service_index;
3947e1e6e7dSMilanka Ringwald                 break;
39570093cf5SMilanka Ringwald             }
396fd39e93aSMilanka Ringwald         }
397fd39e93aSMilanka Ringwald     }
398021192e1SMilanka Ringwald     client->report_index = index;
3997e1e6e7dSMilanka Ringwald     return index;
4007e1e6e7dSMilanka Ringwald }
4017e1e6e7dSMilanka Ringwald 
hids_client_report_query_next_report(hids_client_t * client)4027e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
403021192e1SMilanka Ringwald     client->report_index++;
4047e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
40528da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
406e7bd2dbeSMilanka Ringwald         return true;
40770093cf5SMilanka Ringwald     }
4087e1e6e7dSMilanka Ringwald     return false;
4097e1e6e7dSMilanka Ringwald }
4107e1e6e7dSMilanka Ringwald 
hids_client_report_query_init(hids_client_t * client)4117e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
412021192e1SMilanka Ringwald     client->report_index = 0;
4137e1e6e7dSMilanka Ringwald 
4147e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
41528da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4167e1e6e7dSMilanka Ringwald         return true;
4177e1e6e7dSMilanka Ringwald     }
418e7bd2dbeSMilanka Ringwald     return false;
41970093cf5SMilanka Ringwald }
420ab116b1cSMilanka Ringwald 
hids_client_get_next_notification_report_index(hids_client_t * client)421021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
422021192e1SMilanka Ringwald     uint8_t i;
423021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
424021192e1SMilanka Ringwald 
425021192e1SMilanka Ringwald     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
426021192e1SMilanka Ringwald         hids_client_report_t report = client->reports[i];
427fd39e93aSMilanka Ringwald         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
428fd39e93aSMilanka Ringwald 
429fd39e93aSMilanka Ringwald         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
430fd39e93aSMilanka Ringwald             if (!client->reports[i].boot_report){
431021192e1SMilanka Ringwald                 continue;
432021192e1SMilanka Ringwald             }
433fd39e93aSMilanka Ringwald         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
434fd39e93aSMilanka Ringwald             if (client->reports[i].boot_report){
435fd39e93aSMilanka Ringwald                 continue;
436fd39e93aSMilanka Ringwald             }
437fd39e93aSMilanka Ringwald         }
438fd39e93aSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_INPUT){
439021192e1SMilanka Ringwald             index = i;
440021192e1SMilanka Ringwald         }
441021192e1SMilanka Ringwald     }
442021192e1SMilanka Ringwald     client->report_index = index;
443021192e1SMilanka Ringwald     return index;
444021192e1SMilanka Ringwald }
445021192e1SMilanka Ringwald 
hids_client_report_next_notification_report_index(hids_client_t * client)446021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
447021192e1SMilanka Ringwald     client->report_index++;
448021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
449021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
450021192e1SMilanka Ringwald         return true;
451021192e1SMilanka Ringwald     }
452021192e1SMilanka Ringwald     return false;
453021192e1SMilanka Ringwald }
454021192e1SMilanka Ringwald 
hids_client_report_notifications_init(hids_client_t * client)455021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
456cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
457cd28e7b1SMilanka Ringwald     printf("\nRegister for Notifications: \n");
458cd28e7b1SMilanka Ringwald #endif
459021192e1SMilanka Ringwald     client->report_index = 0;
460021192e1SMilanka Ringwald 
461021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
462021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
463021192e1SMilanka Ringwald         return true;
464021192e1SMilanka Ringwald     }
465021192e1SMilanka Ringwald     return false;
466021192e1SMilanka Ringwald }
467021192e1SMilanka Ringwald 
hids_client_report_next_notifications_configuration_report_index(hids_client_t * client)468cd28e7b1SMilanka Ringwald static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
469cd28e7b1SMilanka Ringwald     client->report_index++;
470cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
471cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
472cd28e7b1SMilanka Ringwald         return true;
473cd28e7b1SMilanka Ringwald     }
474cd28e7b1SMilanka Ringwald     return false;
475cd28e7b1SMilanka Ringwald }
476cd28e7b1SMilanka Ringwald 
hids_client_notifications_configuration_init(hids_client_t * client)477cd28e7b1SMilanka Ringwald static bool hids_client_notifications_configuration_init(hids_client_t * client){
478cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
479cd28e7b1SMilanka Ringwald     printf("\nConfigure for Notifications: \n");
480cd28e7b1SMilanka Ringwald #endif
481cd28e7b1SMilanka Ringwald     client->report_index = 0;
482cd28e7b1SMilanka Ringwald 
483cd28e7b1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
484cd28e7b1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
485cd28e7b1SMilanka Ringwald         return true;
486cd28e7b1SMilanka Ringwald     }
487cd28e7b1SMilanka Ringwald     return false;
488cd28e7b1SMilanka Ringwald }
489cd28e7b1SMilanka Ringwald 
hids_create_client(hci_con_handle_t con_handle,uint16_t cid)490cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
491cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
492cf26c8fbSMilanka Ringwald     if (!client){
493cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
494cf26c8fbSMilanka Ringwald         return NULL;
495cf26c8fbSMilanka Ringwald     }
496cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
497cf26c8fbSMilanka Ringwald     client->cid = cid;
498cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
499fc975d0eSMilanka Ringwald 
500cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
501cf26c8fbSMilanka Ringwald     return client;
502fc975d0eSMilanka Ringwald }
503fc975d0eSMilanka Ringwald 
hids_finalize_client(hids_client_t * client)504cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
505021192e1SMilanka Ringwald     // stop listening
506021192e1SMilanka Ringwald     uint8_t i;
507021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
508021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
509021192e1SMilanka Ringwald     }
510021192e1SMilanka Ringwald 
511da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
512cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
513cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
514fc975d0eSMilanka Ringwald }
515cf26c8fbSMilanka Ringwald 
516cf26c8fbSMilanka Ringwald 
hids_emit_connection_established(hids_client_t * client,uint8_t status)517cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5186bcfb631SMilanka Ringwald     uint8_t event[8];
519cf26c8fbSMilanka Ringwald     int pos = 0;
520cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
521cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
522cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
523cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
524cf26c8fbSMilanka Ringwald     pos += 2;
525cf26c8fbSMilanka Ringwald     event[pos++] = status;
526fd39e93aSMilanka Ringwald     event[pos++] = client->services[0].protocol_mode;
527cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
528cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
529cf26c8fbSMilanka Ringwald }
530cf26c8fbSMilanka Ringwald 
hids_emit_disconnected(btstack_packet_handler_t packet_handler,uint16_t cid)531e5451bcdSMatthias Ringwald static void hids_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){
532e5451bcdSMatthias Ringwald     uint8_t event[5];
533e5451bcdSMatthias Ringwald     int pos = 0;
534e5451bcdSMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
535e5451bcdSMatthias Ringwald     event[pos++] = sizeof(event) - 2;
536e5451bcdSMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_DISCONNECTED;
537e5451bcdSMatthias Ringwald     little_endian_store_16(event, pos, cid);
538e5451bcdSMatthias Ringwald     pos += 2;
539e5451bcdSMatthias Ringwald     btstack_assert(pos == sizeof(event));
540e5451bcdSMatthias Ringwald     (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
541e5451bcdSMatthias Ringwald }
542e5451bcdSMatthias Ringwald 
hids_emit_notifications_configuration(hids_client_t * client)543cd28e7b1SMilanka Ringwald static void hids_emit_notifications_configuration(hids_client_t * client){
544cd28e7b1SMilanka Ringwald     uint8_t event[6];
545cd28e7b1SMilanka Ringwald     int pos = 0;
546cd28e7b1SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
547cd28e7b1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
548cd28e7b1SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
549cd28e7b1SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
550cd28e7b1SMilanka Ringwald     pos += 2;
551cd28e7b1SMilanka Ringwald     event[pos++] = client->value;
552cd28e7b1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
553cd28e7b1SMilanka Ringwald }
554cd28e7b1SMilanka Ringwald 
hids_client_setup_report_event(uint8_t subevent,hids_client_t * client,uint8_t report_index,uint8_t * buffer,uint16_t report_len)555e3bccb1cSMatthias Ringwald static uint16_t hids_client_setup_report_event(uint8_t subevent, hids_client_t *client, uint8_t report_index, uint8_t *buffer,
556e3bccb1cSMatthias Ringwald                                uint16_t report_len) {
557021192e1SMilanka Ringwald     uint16_t pos = 0;
558021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
559021192e1SMilanka Ringwald     pos++;  // skip len
560e3bccb1cSMatthias Ringwald     buffer[pos++] = subevent;
561021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
562021192e1SMilanka Ringwald     pos += 2;
563021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
564021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
565a2d3931bSMatthias Ringwald     little_endian_store_16(buffer, pos, report_len);
566021192e1SMilanka Ringwald     pos += 2;
567a2d3931bSMatthias Ringwald     buffer[1] = pos + report_len - 2;
568a2d3931bSMatthias Ringwald     return pos;
569a2d3931bSMatthias Ringwald }
57083d7ed1cSMilanka Ringwald 
hids_client_setup_report_event_with_report_id(hids_client_t * client,uint8_t report_index,uint8_t * buffer,uint16_t report_len)571a2d3931bSMatthias Ringwald static void hids_client_setup_report_event_with_report_id(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
572e3bccb1cSMatthias Ringwald     uint16_t pos = hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT, client, report_index, buffer,
573e3bccb1cSMatthias Ringwald                                                   report_len + 1);
574a2d3931bSMatthias Ringwald     buffer[pos] = client->reports[report_index].report_id;
575021192e1SMilanka Ringwald }
576021192e1SMilanka Ringwald 
hids_client_emit_hid_information_event(hids_client_t * client,const uint8_t * value,uint16_t value_len)577f4d3b82aSMilanka Ringwald static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
578f4d3b82aSMilanka Ringwald     if (value_len != 4) return;
579f4d3b82aSMilanka Ringwald 
580f4d3b82aSMilanka Ringwald     uint8_t event[11];
581f4d3b82aSMilanka Ringwald     int pos = 0;
582f4d3b82aSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
583f4d3b82aSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
584f4d3b82aSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
585f4d3b82aSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
586f4d3b82aSMilanka Ringwald     pos += 2;
587f4d3b82aSMilanka Ringwald     event[pos++] = client->service_index;
588f4d3b82aSMilanka Ringwald 
589f4d3b82aSMilanka Ringwald     memcpy(event+pos, value, 3);
590f4d3b82aSMilanka Ringwald     pos += 3;
591f4d3b82aSMilanka Ringwald     event[pos++] = (value[3] & 0x02) >> 1;
592f4d3b82aSMilanka Ringwald     event[pos++] = value[3] & 0x01;
593f4d3b82aSMilanka Ringwald 
594f4d3b82aSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
595f4d3b82aSMilanka Ringwald }
596f4d3b82aSMilanka Ringwald 
hids_client_emit_protocol_mode_event(hids_client_t * client,const uint8_t * value,uint16_t value_len)597af2241c2SMilanka Ringwald static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
598af2241c2SMilanka Ringwald     if (value_len != 1) return;
599af2241c2SMilanka Ringwald 
600af2241c2SMilanka Ringwald     uint8_t event[11];
601af2241c2SMilanka Ringwald     int pos = 0;
602af2241c2SMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
603af2241c2SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
604af2241c2SMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
605af2241c2SMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
606af2241c2SMilanka Ringwald     pos += 2;
607af2241c2SMilanka Ringwald     event[pos++] = client->service_index;
608af2241c2SMilanka Ringwald     event[pos++] = value[0];
609af2241c2SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
610af2241c2SMilanka Ringwald }
611f4d3b82aSMilanka Ringwald 
handle_notification_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)61283d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
613021192e1SMilanka Ringwald     UNUSED(packet_type);
614021192e1SMilanka Ringwald     UNUSED(channel);
61583d7ed1cSMilanka Ringwald 
616021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
617021192e1SMilanka Ringwald 
618021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
61917229983SMatthias Ringwald     if (client == NULL) return;
620021192e1SMilanka Ringwald 
621021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
622021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
623021192e1SMilanka Ringwald         return;
624021192e1SMilanka Ringwald     }
625021192e1SMilanka Ringwald 
626d41c2d28SMatthias Ringwald     // GATT_EVENT_NOTIFICATION has HID report data at offset 12
627d41c2d28SMatthias Ringwald     // - see gatt_event_notification_get_value()
628d41c2d28SMatthias Ringwald     //
629d41c2d28SMatthias Ringwald     // GATTSERVICE_SUBEVENT_HID_REPORT has HID report data at offset 10
630d41c2d28SMatthias Ringwald     // - see gattservice_subevent_hid_report_get_report() and add 1 for the inserted Report ID
631d41c2d28SMatthias Ringwald     //
632d41c2d28SMatthias Ringwald     // => use existing packet from offset 2 = 12 - 10 to setup event
633d41c2d28SMatthias Ringwald     const int16_t offset = 2;
634d41c2d28SMatthias Ringwald 
635d41c2d28SMatthias Ringwald     uint8_t * in_place_event = &packet[offset];
636a2d3931bSMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
637a2d3931bSMatthias Ringwald                                                   gatt_event_notification_get_value_length(packet));
638d41c2d28SMatthias Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size - offset);
639021192e1SMilanka Ringwald }
640021192e1SMilanka Ringwald 
handle_report_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)64183d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
64283d7ed1cSMilanka Ringwald     UNUSED(packet_type);
64383d7ed1cSMilanka Ringwald     UNUSED(channel);
64483d7ed1cSMilanka Ringwald 
64583d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
64683d7ed1cSMilanka Ringwald 
64783d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
64817229983SMatthias Ringwald     if (client == NULL) return;
64983d7ed1cSMilanka Ringwald 
65083d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
65183d7ed1cSMilanka Ringwald         return;
65283d7ed1cSMilanka Ringwald     }
65383d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
65483d7ed1cSMilanka Ringwald 
65583d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
65683d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
65783d7ed1cSMilanka Ringwald         return;
65883d7ed1cSMilanka Ringwald     }
65983d7ed1cSMilanka Ringwald 
660*61776ecdSMatthias Ringwald     // GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT has HID report data at offset 12
661*61776ecdSMatthias Ringwald     // - see gatt_event_characteristic_value_query_result_get_value()
662*61776ecdSMatthias Ringwald     //
663*61776ecdSMatthias Ringwald     // GATTSERVICE_SUBEVENT_HID_REPORT has HID report data at offset 10
664*61776ecdSMatthias Ringwald     // - see gattservice_subevent_hid_report_get_report() and add 1 for the inserted Report ID
665*61776ecdSMatthias Ringwald     //
666*61776ecdSMatthias Ringwald     // => use existing packet from offset 2 = 12 - 10 to setup event
667*61776ecdSMatthias Ringwald     const int16_t offset = 2;
668*61776ecdSMatthias Ringwald 
669*61776ecdSMatthias Ringwald     uint8_t * in_place_event = &packet[offset];
67084d4ab66SMatthias Ringwald     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
67184d4ab66SMatthias Ringwald                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
672*61776ecdSMatthias Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size - offset);
67383d7ed1cSMilanka Ringwald }
674cf26c8fbSMilanka Ringwald 
hids_run_for_client(hids_client_t * client)675cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
676cf26c8fbSMilanka Ringwald     uint8_t att_status;
6776bcfb631SMilanka Ringwald     gatt_client_service_t service;
6786bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
679cf26c8fbSMilanka Ringwald 
680cf26c8fbSMilanka Ringwald     switch (client->state){
681cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
682556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
683556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
684556456ccSMilanka Ringwald #endif
685cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
68619146789SMilanka Ringwald 
68719146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
688cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
689cf26c8fbSMilanka Ringwald             UNUSED(att_status);
690cf26c8fbSMilanka Ringwald             break;
691cf26c8fbSMilanka Ringwald 
692cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
693556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
694556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
695556456ccSMilanka Ringwald #endif
6966bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6976bcfb631SMilanka Ringwald 
6986bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6996bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
70019146789SMilanka Ringwald 
70119146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7026bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
7036bcfb631SMilanka Ringwald 
7046bcfb631SMilanka Ringwald             UNUSED(att_status);
7056bcfb631SMilanka Ringwald             break;
7066bcfb631SMilanka Ringwald 
70728da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
708556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
709556456ccSMilanka 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);
710556456ccSMilanka Ringwald #endif
71128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
712556456ccSMilanka Ringwald 
71319146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
71419146789SMilanka 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);
715e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
716e7bd2dbeSMilanka Ringwald             break;
717e7bd2dbeSMilanka Ringwald 
718e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
719556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
720556456ccSMilanka 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);
721556456ccSMilanka Ringwald #endif
722e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
723e7bd2dbeSMilanka Ringwald 
7243322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
7253322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
7263322b222SMilanka Ringwald 
72719146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
728e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
729e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
730e7bd2dbeSMilanka Ringwald             break;
731e7bd2dbeSMilanka Ringwald 
73228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
733556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
73419146789SMilanka 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);
735556456ccSMilanka Ringwald #endif
73628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
737e7bd2dbeSMilanka Ringwald 
73819146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
73919146789SMilanka 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);
740e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
741e7bd2dbeSMilanka Ringwald             break;
742e7bd2dbeSMilanka Ringwald 
7433322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
744556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
74519146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
746556456ccSMilanka Ringwald #endif
7473322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
748e7bd2dbeSMilanka Ringwald 
7493322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7503322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
75119146789SMilanka Ringwald 
75219146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7533322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
754e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
755e7bd2dbeSMilanka Ringwald             break;
756e7bd2dbeSMilanka Ringwald 
75728da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
758556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
75983d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
760556456ccSMilanka Ringwald                 client->report_index,
76183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
76283d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
763556456ccSMilanka Ringwald #endif
76428da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
765af2241c2SMilanka Ringwald             client->handle = 0;
766556456ccSMilanka Ringwald 
767556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
768556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
769556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
770556456ccSMilanka Ringwald 
77119146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
77270093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
77370093cf5SMilanka Ringwald             UNUSED(att_status);
77470093cf5SMilanka Ringwald             break;
77570093cf5SMilanka Ringwald 
77628da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
77728da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
77870093cf5SMilanka Ringwald 
77919146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
780af2241c2SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
781af2241c2SMilanka Ringwald             client->handle = 0;
78270093cf5SMilanka Ringwald             UNUSED(att_status);
78370093cf5SMilanka Ringwald             break;
78470093cf5SMilanka Ringwald 
785cd28e7b1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
786cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
787cd28e7b1SMilanka Ringwald             if (client->value > 0){
788cd28e7b1SMilanka Ringwald                 printf("    Notification configuration enable ");
789cd28e7b1SMilanka Ringwald             } else {
790cd28e7b1SMilanka Ringwald                 printf("    Notification configuration disable ");
791cd28e7b1SMilanka Ringwald             }
792cd28e7b1SMilanka Ringwald             printf("[%d, %d, 0x%04X]:\n",
793cd28e7b1SMilanka Ringwald                 client->report_index,
794cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
795cd28e7b1SMilanka Ringwald #endif
796cd28e7b1SMilanka Ringwald 
797cd28e7b1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
798cd28e7b1SMilanka Ringwald 
799cd28e7b1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
800cd28e7b1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
801cd28e7b1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
802cd28e7b1SMilanka Ringwald 
803cd28e7b1SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
804cd28e7b1SMilanka Ringwald 
805cd28e7b1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
806cd28e7b1SMilanka Ringwald 
807cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
808cd28e7b1SMilanka Ringwald                 switch(client->value){
809cd28e7b1SMilanka Ringwald                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
810cd28e7b1SMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(
811cd28e7b1SMilanka Ringwald                             &client->reports[client->report_index].notification_listener,
812cd28e7b1SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
813cd28e7b1SMilanka Ringwald                         break;
814cd28e7b1SMilanka Ringwald                     default:
815cd28e7b1SMilanka Ringwald                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
816cd28e7b1SMilanka Ringwald                         break;
817cd28e7b1SMilanka Ringwald                 }
818cd28e7b1SMilanka Ringwald             } else {
819cd28e7b1SMilanka Ringwald                 if (hids_client_report_next_notifications_configuration_report_index(client)){
820cd28e7b1SMilanka Ringwald                     hids_run_for_client(client);
821cd28e7b1SMilanka Ringwald                     break;
822cd28e7b1SMilanka Ringwald                 }
823cd28e7b1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
824cd28e7b1SMilanka Ringwald             }
825cd28e7b1SMilanka Ringwald             break;
826cd28e7b1SMilanka Ringwald 
827021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
828cd28e7b1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
829cd28e7b1SMilanka Ringwald             printf("    Notification enable [%d, %d, 0x%04X]:\n",
830cd28e7b1SMilanka Ringwald                 client->report_index,
831cd28e7b1SMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
832cd28e7b1SMilanka Ringwald #endif
833021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
834021192e1SMilanka Ringwald 
835021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
836021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
837021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
838021192e1SMilanka Ringwald 
83919146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
840021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
841021192e1SMilanka Ringwald 
842cd28e7b1SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
843cd28e7b1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
844cd28e7b1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
845cd28e7b1SMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
846cd28e7b1SMilanka Ringwald             } else {
847021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
848021192e1SMilanka Ringwald                     hids_run_for_client(client);
849021192e1SMilanka Ringwald                     break;
850021192e1SMilanka Ringwald                 }
851021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
852021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
853021192e1SMilanka Ringwald             }
854021192e1SMilanka Ringwald             break;
855021192e1SMilanka Ringwald 
85683d7ed1cSMilanka Ringwald 
8576d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
85883d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8596d6f7efcSMilanka Ringwald             printf("    Write report [%d, %d, 0x%04X]:\n",
86083d7ed1cSMilanka Ringwald                 client->report_index,
86183d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
86283d7ed1cSMilanka Ringwald #endif
86383d7ed1cSMilanka Ringwald 
8646d6f7efcSMilanka Ringwald             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
86583d7ed1cSMilanka Ringwald 
8666d6f7efcSMilanka Ringwald             // see GATT_EVENT_QUERY_COMPLETE for end of write
8676d6f7efcSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic(
86884d4ab66SMatthias Ringwald                 &handle_gatt_client_event, client->con_handle,
86983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
87083d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
87183d7ed1cSMilanka Ringwald             UNUSED(att_status);
87283d7ed1cSMilanka Ringwald             break;
87383d7ed1cSMilanka Ringwald 
87483d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
87583d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
876f4d3b82aSMilanka Ringwald             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
877f4d3b82aSMilanka Ringwald                 client->report_index,
87883d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
87983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
88083d7ed1cSMilanka Ringwald #endif
88183d7ed1cSMilanka Ringwald 
88283d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
883f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
88483d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
88583d7ed1cSMilanka Ringwald                 &handle_report_event,
88683d7ed1cSMilanka Ringwald                 client->con_handle,
88783d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
88883d7ed1cSMilanka Ringwald             UNUSED(att_status);
88983d7ed1cSMilanka Ringwald             break;
89083d7ed1cSMilanka Ringwald 
89183d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
89283d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
89383d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
89483d7ed1cSMilanka Ringwald 
895f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
89683d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
89783d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
89883d7ed1cSMilanka Ringwald                 client->con_handle,
89983d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
90083d7ed1cSMilanka Ringwald 
90183d7ed1cSMilanka Ringwald             break;
90283d7ed1cSMilanka Ringwald #endif
903af2241c2SMilanka Ringwald         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
904af2241c2SMilanka Ringwald             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
90583d7ed1cSMilanka Ringwald 
906f4d3b82aSMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
907f4d3b82aSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
908f4d3b82aSMilanka Ringwald                 &handle_gatt_client_event,
909f4d3b82aSMilanka Ringwald                 client->con_handle,
910af2241c2SMilanka Ringwald                 client->handle);
911f4d3b82aSMilanka Ringwald             break;
9126d6f7efcSMilanka Ringwald 
913fd39e93aSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
9146d6f7efcSMilanka Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9151fa7278eSMatthias Ringwald             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
9161fa7278eSMatthias Ringwald             client->write_without_response_request.context = client;
9171fa7278eSMatthias Ringwald             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
9181fa7278eSMatthias Ringwald             break;
9191fa7278eSMatthias Ringwald 
9201fa7278eSMatthias Ringwald         default:
9211fa7278eSMatthias Ringwald             break;
9221fa7278eSMatthias Ringwald     }
9231fa7278eSMatthias Ringwald }
9241fa7278eSMatthias Ringwald 
hids_client_handle_can_write_without_reponse(void * context)9251fa7278eSMatthias Ringwald static void hids_client_handle_can_write_without_reponse(void * context) {
9261fa7278eSMatthias Ringwald     hids_client_t *client = (hids_client_t *) context;
9271fa7278eSMatthias Ringwald     uint8_t att_status;
9281fa7278eSMatthias Ringwald     switch (client->state){
9291fa7278eSMatthias Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
9301fa7278eSMatthias Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(
9311fa7278eSMatthias Ringwald                 client->con_handle,
9321fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
9331fa7278eSMatthias Ringwald 
9341fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9351fa7278eSMatthias Ringwald             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
9361fa7278eSMatthias Ringwald #endif
9371fa7278eSMatthias Ringwald 
9381fa7278eSMatthias Ringwald             if (att_status == ATT_ERROR_SUCCESS){
9391fa7278eSMatthias Ringwald                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
9401fa7278eSMatthias Ringwald                 if ((client->service_index + 1) < client->num_instances){
9411fa7278eSMatthias Ringwald                     client->service_index++;
9421fa7278eSMatthias Ringwald                     hids_run_for_client(client);
9431fa7278eSMatthias Ringwald                     break;
9441fa7278eSMatthias Ringwald                 }
9451fa7278eSMatthias Ringwald             }
9461fa7278eSMatthias Ringwald 
9471fa7278eSMatthias Ringwald             // read UUIDS for external characteristics
9481fa7278eSMatthias Ringwald             if (hids_client_report_map_uuid_query_init(client)){
9491fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9501fa7278eSMatthias Ringwald                 break;
9511fa7278eSMatthias Ringwald             }
9521fa7278eSMatthias Ringwald 
9531fa7278eSMatthias Ringwald             // discover characteristic descriptor for all Report characteristics,
9541fa7278eSMatthias Ringwald             // then read value of characteristic descriptor to get Report ID
9551fa7278eSMatthias Ringwald             if (hids_client_report_query_init(client)){
9561fa7278eSMatthias Ringwald                 hids_run_for_client(client);
9571fa7278eSMatthias Ringwald                 break;
9581fa7278eSMatthias Ringwald             }
9591fa7278eSMatthias Ringwald 
9601fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9611fa7278eSMatthias Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9621fa7278eSMatthias Ringwald             break;
9631fa7278eSMatthias Ringwald 
9641fa7278eSMatthias Ringwald         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
9651fa7278eSMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
9661fa7278eSMatthias Ringwald             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
9671fa7278eSMatthias Ringwald #endif
9681fa7278eSMatthias Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
9691fa7278eSMatthias Ringwald             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
9706d6f7efcSMilanka Ringwald             break;
9716d6f7efcSMilanka Ringwald 
9726bcfb631SMilanka Ringwald         default:
9736bcfb631SMilanka Ringwald             break;
9746bcfb631SMilanka Ringwald     }
9756bcfb631SMilanka Ringwald }
9766bcfb631SMilanka Ringwald 
handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)977cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
978cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
979cf26c8fbSMilanka Ringwald     UNUSED(channel);
980cf26c8fbSMilanka Ringwald     UNUSED(size);
981cf26c8fbSMilanka Ringwald 
9826bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
983b60c1680SMatthias Ringwald     uint8_t status;
9846bcfb631SMilanka Ringwald     gatt_client_service_t service;
9856bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
98670093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
987ab116b1cSMilanka Ringwald 
988021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
989021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
990e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
9913322b222SMilanka Ringwald     uint8_t i;
9923322b222SMilanka Ringwald     uint8_t report_index;
993da142a6fSMilanka Ringwald 
994f4d3b82aSMilanka Ringwald     const uint8_t * value;
995f4d3b82aSMilanka Ringwald     uint16_t value_len;
996cf26c8fbSMilanka Ringwald 
997cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
998cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
999cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
100017229983SMatthias Ringwald             if (client == NULL) break;
1001cf26c8fbSMilanka Ringwald 
1002021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
1003021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
10046bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
1005021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
1006021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
10076bcfb631SMilanka Ringwald                 client->num_instances++;
1008021192e1SMilanka Ringwald 
1009708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1010708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
1011708c69d2SMilanka Ringwald #endif
1012021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
1013021192e1SMilanka Ringwald             }  else {
1014021192e1SMilanka 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);
1015da142a6fSMilanka Ringwald             }
10166bcfb631SMilanka Ringwald             break;
10176bcfb631SMilanka Ringwald 
10186bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
10196bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
102017229983SMatthias Ringwald             if (client == NULL) break;
102117229983SMatthias Ringwald 
10226bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
102319146789SMilanka Ringwald 
102419146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
102519146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
102619146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
102719146789SMilanka Ringwald                     break;
102819146789SMilanka Ringwald                 }
102919146789SMilanka Ringwald             }
103019146789SMilanka Ringwald 
103119146789SMilanka Ringwald             switch (characteristic.uuid16){
103219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1033af2241c2SMilanka Ringwald                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
103419146789SMilanka Ringwald                     break;
103519146789SMilanka Ringwald 
103619146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
103719146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
103819146789SMilanka Ringwald                     break;
103919146789SMilanka Ringwald 
104019146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
104119146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
104219146789SMilanka Ringwald                     break;
104319146789SMilanka Ringwald 
104419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1045f4d3b82aSMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
104619146789SMilanka Ringwald                     break;
104719146789SMilanka Ringwald 
104819146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
104919146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
105019146789SMilanka Ringwald                     break;
105119146789SMilanka Ringwald 
105219146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
105319146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
105419146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
105519146789SMilanka Ringwald                     break;
105619146789SMilanka Ringwald 
105719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1058f4d3b82aSMilanka Ringwald                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
105919146789SMilanka Ringwald                     break;
106019146789SMilanka Ringwald 
106119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1062f4d3b82aSMilanka Ringwald                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
106319146789SMilanka Ringwald                     break;
106419146789SMilanka Ringwald 
106519146789SMilanka Ringwald                 default:
106619146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
106719146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
106819146789SMilanka Ringwald #endif
106919146789SMilanka Ringwald                     return;
107019146789SMilanka Ringwald             }
107119146789SMilanka Ringwald 
107219146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
107319146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
107419146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
107519146789SMilanka Ringwald                 characteristic.start_handle,
107619146789SMilanka Ringwald                 characteristic.properties,
107719146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
107819146789SMilanka Ringwald                 client->service_index);
107919146789SMilanka Ringwald 
108019146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
108119146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
108219146789SMilanka Ringwald             }
108319146789SMilanka Ringwald             printf("\n");
108419146789SMilanka Ringwald #endif
1085cf26c8fbSMilanka Ringwald             break;
1086cf26c8fbSMilanka Ringwald 
10878cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1088e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
10898cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
109017229983SMatthias Ringwald             if (client == NULL) break;
1091da142a6fSMilanka Ringwald 
1092f4d3b82aSMilanka Ringwald             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1093f4d3b82aSMilanka Ringwald             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
10948cec2b74SMilanka Ringwald 
1095556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
10968cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1097f4d3b82aSMilanka Ringwald             printf_hexdump(value, value_len);
1098556456ccSMilanka Ringwald #endif
1099f4d3b82aSMilanka Ringwald             for (i = 0; i < value_len; i++){
1100f4d3b82aSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1101da142a6fSMilanka Ringwald                 if (!stored){
1102da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1103da142a6fSMilanka Ringwald                     break;
1104da142a6fSMilanka Ringwald                 }
1105da142a6fSMilanka Ringwald             }
1106e7bd2dbeSMilanka Ringwald             break;
1107e7bd2dbeSMilanka Ringwald 
110870093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
110970093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
111017229983SMatthias Ringwald             if (client == NULL) break;
111117229983SMatthias Ringwald 
111270093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
111370093cf5SMilanka Ringwald 
1114e7bd2dbeSMilanka Ringwald             switch (client->state) {
1115e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1116e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
1117e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
111819146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
11193322b222SMilanka Ringwald 
1120556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1121556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1122556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1123556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
1124556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
1125556456ccSMilanka Ringwald                                 client->service_index, report_index);
1126556456ccSMilanka Ringwald                         }
1127556456ccSMilanka Ringwald #endif
1128e7bd2dbeSMilanka Ringwald                     }
1129e7bd2dbeSMilanka Ringwald                     break;
113028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
113170093cf5SMilanka Ringwald                     // setup for descriptor value query
113270093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1133af2241c2SMilanka Ringwald                         client->handle = characteristic_descriptor.handle;
1134556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1135556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1136556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1137556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
113819146789SMilanka Ringwald #endif
1139556456ccSMilanka Ringwald                     }
1140556456ccSMilanka Ringwald 
114119146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1142556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
114383d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1144556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1145556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1146556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1147556456ccSMilanka Ringwald                     }
1148556456ccSMilanka Ringwald #endif
114970093cf5SMilanka Ringwald                     break;
1150556456ccSMilanka Ringwald 
1151e7bd2dbeSMilanka Ringwald                 default:
1152e7bd2dbeSMilanka Ringwald                     break;
1153e7bd2dbeSMilanka Ringwald             }
1154e7bd2dbeSMilanka Ringwald             break;
115570093cf5SMilanka Ringwald 
115683d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
115783d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
115817229983SMatthias Ringwald             if (client == NULL) break;
115983d7ed1cSMilanka Ringwald 
1160f4d3b82aSMilanka Ringwald             value = gatt_event_characteristic_value_query_result_get_value(packet);
1161f4d3b82aSMilanka Ringwald             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1162f4d3b82aSMilanka Ringwald 
1163af2241c2SMilanka Ringwald 
1164f4d3b82aSMilanka Ringwald             switch (client->state){
1165f4d3b82aSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1166f4d3b82aSMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
116783d7ed1cSMilanka Ringwald                     printf("    Received CCC value: ");
1168f4d3b82aSMilanka Ringwald                     printf_hexdump(value,  value_len);
116983d7ed1cSMilanka Ringwald                     break;
117083d7ed1cSMilanka Ringwald #endif
1171af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1172af2241c2SMilanka Ringwald                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1173af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1174f4d3b82aSMilanka Ringwald                         hids_client_emit_hid_information_event(client, value, value_len);
1175f4d3b82aSMilanka Ringwald                         break;
1176af2241c2SMilanka Ringwald                     }
1177af2241c2SMilanka Ringwald                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1178af2241c2SMilanka Ringwald                         hids_client_emit_protocol_mode_event(client, value, value_len);
1179af2241c2SMilanka Ringwald                         break;
1180af2241c2SMilanka Ringwald                     }
1181af2241c2SMilanka Ringwald                     break;
1182af2241c2SMilanka Ringwald                 }
1183f4d3b82aSMilanka Ringwald                 default:
1184f4d3b82aSMilanka Ringwald                     break;
1185f4d3b82aSMilanka Ringwald             }
1186f4d3b82aSMilanka Ringwald 
1187f4d3b82aSMilanka Ringwald             break;
118883d7ed1cSMilanka Ringwald 
118970093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
119070093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
119117229983SMatthias Ringwald             if (client == NULL) break;
1192e7bd2dbeSMilanka Ringwald 
1193e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1194e7bd2dbeSMilanka Ringwald                 break;
1195e7bd2dbeSMilanka Ringwald             }
11967e1e6e7dSMilanka Ringwald 
1197e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1198e7bd2dbeSMilanka Ringwald             switch (client->state) {
119928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1200e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
120119146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
12023322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
120319146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1204556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
120519146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
120619146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1207556456ccSMilanka Ringwald #endif
12083322b222SMilanka Ringwald                     }
1209e7bd2dbeSMilanka Ringwald                     break;
1210e7bd2dbeSMilanka Ringwald 
121128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
121219146789SMilanka Ringwald 
121319146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1214021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1215021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
121619146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
121783d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1218021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1219021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
122083d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
122119146789SMilanka Ringwald     #endif
122219146789SMilanka Ringwald                     }
1223e7bd2dbeSMilanka Ringwald                     break;
1224e7bd2dbeSMilanka Ringwald 
1225e7bd2dbeSMilanka Ringwald                 default:
1226e7bd2dbeSMilanka Ringwald                     break;
122770093cf5SMilanka Ringwald             }
122870093cf5SMilanka Ringwald             break;
122970093cf5SMilanka Ringwald 
1230cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1231cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
123217229983SMatthias Ringwald             if (client == NULL) break;
1233cf26c8fbSMilanka Ringwald 
123445cccc7dSMatthias Ringwald             status = gatt_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet));
1235cf26c8fbSMilanka Ringwald 
1236cf26c8fbSMilanka Ringwald             switch (client->state){
1237cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1238b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1239b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
12406bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12415fa2c39aSMilanka Ringwald                         return;
12426bcfb631SMilanka Ringwald                     }
12436bcfb631SMilanka Ringwald 
12446bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
12456bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
12466bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12475fa2c39aSMilanka Ringwald                         return;
12486bcfb631SMilanka Ringwald                     }
12496bcfb631SMilanka Ringwald 
12506bcfb631SMilanka Ringwald                     client->service_index = 0;
125119146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
12526bcfb631SMilanka Ringwald                     break;
12536bcfb631SMilanka Ringwald 
12546bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1255b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1256b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
12576bcfb631SMilanka Ringwald                         hids_finalize_client(client);
12585fa2c39aSMilanka Ringwald                         return;
12596bcfb631SMilanka Ringwald                     }
12606bcfb631SMilanka Ringwald 
126170093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
126270093cf5SMilanka Ringwald                         // discover characteristics of next service
126370093cf5SMilanka Ringwald                         client->service_index++;
126470093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1265021192e1SMilanka Ringwald                         break;
1266021192e1SMilanka Ringwald                     }
1267021192e1SMilanka Ringwald 
1268fd39e93aSMilanka Ringwald                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1269fd39e93aSMilanka Ringwald 
1270021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1271021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1272fd39e93aSMilanka Ringwald                             for (i = 0; i < client->num_instances; i++){
1273fd39e93aSMilanka Ringwald                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1274021192e1SMilanka Ringwald                             }
12757e1e6e7dSMilanka Ringwald                             // 1. we need to get HID Descriptor and
12767e1e6e7dSMilanka Ringwald                             // 2. get external Report characteristics if referenced from Report Map
12777e1e6e7dSMilanka Ringwald                             if (hids_client_report_map_query_init(client)){
127870093cf5SMilanka Ringwald                                 break;
127970093cf5SMilanka Ringwald                             }
128070093cf5SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
128170093cf5SMilanka Ringwald                             hids_finalize_client(client);
1282fd39e93aSMilanka Ringwald                             return;
1283fd39e93aSMilanka Ringwald 
1284fd39e93aSMilanka Ringwald                         default:
1285fd39e93aSMilanka Ringwald                             // set boot mode
1286fd39e93aSMilanka Ringwald                             client->service_index = 0;
1287fd39e93aSMilanka Ringwald                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1288fd39e93aSMilanka Ringwald                             break;
1289fd39e93aSMilanka Ringwald                     }
12906bcfb631SMilanka Ringwald                     break;
12916bcfb631SMilanka Ringwald 
12923322b222SMilanka Ringwald 
129328da36a6SMilanka Ringwald                 // HID descriptor found
129428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1295b60c1680SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
1296b60c1680SMatthias Ringwald                         hids_emit_connection_established(client, status);
1297e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
12985fa2c39aSMilanka Ringwald                         return;
1299e7bd2dbeSMilanka Ringwald                     }
1300e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1301e7bd2dbeSMilanka Ringwald                     break;
1302e7bd2dbeSMilanka Ringwald 
130328da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1304e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
13053322b222SMilanka Ringwald                     // go for next report map
13063322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1307e7bd2dbeSMilanka Ringwald                         break;
1308e7bd2dbeSMilanka Ringwald                     }
1309e7bd2dbeSMilanka Ringwald 
13103322b222SMilanka Ringwald                     // read UUIDS for external characteristics
13113322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1312e7bd2dbeSMilanka Ringwald                         break;
1313e7bd2dbeSMilanka Ringwald                     }
1314e7bd2dbeSMilanka Ringwald 
1315e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1316e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13177e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1318e7bd2dbeSMilanka Ringwald                         break;
1319e7bd2dbeSMilanka Ringwald                     }
1320e7bd2dbeSMilanka Ringwald 
1321e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1322e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13235fa2c39aSMilanka Ringwald                     return;
1324e7bd2dbeSMilanka Ringwald 
132528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1326e7bd2dbeSMilanka Ringwald                     // go for next map report
13273322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1328e7bd2dbeSMilanka Ringwald                         break;
1329e7bd2dbeSMilanka Ringwald                     }
1330e7bd2dbeSMilanka Ringwald 
13313322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
13323322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1333e7bd2dbeSMilanka Ringwald                     break;
1334e7bd2dbeSMilanka Ringwald 
13353322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1336e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1337e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
13387e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1339e7bd2dbeSMilanka Ringwald                         break;
1340e7bd2dbeSMilanka Ringwald                     }
1341e7bd2dbeSMilanka Ringwald 
1342e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1343e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
13445fa2c39aSMilanka Ringwald                     return;
1345e7bd2dbeSMilanka Ringwald 
134628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1347af2241c2SMilanka Ringwald                     if (client->handle != 0){
134828da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
134970093cf5SMilanka Ringwald                         break;
135070093cf5SMilanka Ringwald                     }
135170093cf5SMilanka Ringwald                     // go for next report
13527e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
135370093cf5SMilanka Ringwald                         break;
135470093cf5SMilanka Ringwald                     }
1355835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
135670093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
135770093cf5SMilanka Ringwald                     break;
135870093cf5SMilanka Ringwald 
135928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
136070093cf5SMilanka Ringwald                     // go for next report
13617e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
136270093cf5SMilanka Ringwald                         break;
136370093cf5SMilanka Ringwald                     }
1364021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1365021192e1SMilanka Ringwald                         break;
1366021192e1SMilanka Ringwald                     }
1367835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
136870093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
136970093cf5SMilanka Ringwald                     break;
137070093cf5SMilanka Ringwald 
1371021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1372021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
13732901a9b7SMilanka Ringwald                         break;
13742901a9b7SMilanka Ringwald                     }
1375835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
13766bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
13776bcfb631SMilanka Ringwald                     break;
1378f4d3b82aSMilanka Ringwald 
1379cd28e7b1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1380cd28e7b1SMilanka Ringwald                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1381cd28e7b1SMilanka Ringwald                         break;
1382cd28e7b1SMilanka Ringwald                     }
1383cd28e7b1SMilanka Ringwald                     hids_emit_notifications_configuration(client);
1384cd28e7b1SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1385cd28e7b1SMilanka Ringwald                     break;
1386cd28e7b1SMilanka Ringwald 
1387835a13f1SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1388835a13f1SMilanka Ringwald                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1389835a13f1SMilanka Ringwald                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1390835a13f1SMilanka Ringwald                     break;
1391835a13f1SMilanka Ringwald #endif
1392f4d3b82aSMilanka Ringwald 
1393af2241c2SMilanka Ringwald                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1394f4d3b82aSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1395f4d3b82aSMilanka Ringwald                     break;
1396f4d3b82aSMilanka Ringwald 
139784d4ab66SMatthias Ringwald                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
139884d4ab66SMatthias Ringwald                     {
139984d4ab66SMatthias Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
140084d4ab66SMatthias Ringwald 
140184d4ab66SMatthias Ringwald                         // emit empty report to signal done
140284d4ab66SMatthias Ringwald                         uint8_t event[9];
1403e3bccb1cSMatthias Ringwald                         hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1404e3bccb1cSMatthias Ringwald                                                        client->report_index, event, 0);
140584d4ab66SMatthias Ringwald                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
140684d4ab66SMatthias Ringwald                     }
140784d4ab66SMatthias Ringwald                     break;
14086d6f7efcSMilanka Ringwald 
1409cf26c8fbSMilanka Ringwald                 default:
1410cf26c8fbSMilanka Ringwald                     break;
1411cf26c8fbSMilanka Ringwald             }
1412cf26c8fbSMilanka Ringwald             break;
1413cf26c8fbSMilanka Ringwald 
1414cf26c8fbSMilanka Ringwald         default:
1415cf26c8fbSMilanka Ringwald             break;
1416cf26c8fbSMilanka Ringwald     }
14176bcfb631SMilanka Ringwald 
14186bcfb631SMilanka Ringwald     if (client != NULL){
14196bcfb631SMilanka Ringwald         hids_run_for_client(client);
14206bcfb631SMilanka Ringwald     }
1421cf26c8fbSMilanka Ringwald }
1422cf26c8fbSMilanka Ringwald 
handle_hci_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)1423e5451bcdSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1424e5451bcdSMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
1425e5451bcdSMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
1426e5451bcdSMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
1427e5451bcdSMatthias Ringwald 
1428e5451bcdSMatthias Ringwald     hci_con_handle_t con_handle;
1429e5451bcdSMatthias Ringwald     hids_client_t * client;
1430e5451bcdSMatthias Ringwald 
1431e5451bcdSMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
1432e5451bcdSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
1433e5451bcdSMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
1434e5451bcdSMatthias Ringwald             client = hids_get_client_for_con_handle(con_handle);
1435e5451bcdSMatthias Ringwald             if (client != NULL){
1436e5451bcdSMatthias Ringwald                 // emit disconnected
1437e5451bcdSMatthias Ringwald                 btstack_packet_handler_t packet_handler = client->client_handler;
1438e5451bcdSMatthias Ringwald                 uint16_t cid = client->cid;
1439e5451bcdSMatthias Ringwald                 hids_emit_disconnected(packet_handler, cid);
1440e5451bcdSMatthias Ringwald                 // finalize
1441e5451bcdSMatthias Ringwald                 hids_finalize_client(client);
1442e5451bcdSMatthias Ringwald             }
1443e5451bcdSMatthias Ringwald             break;
1444e5451bcdSMatthias Ringwald         default:
1445e5451bcdSMatthias Ringwald             break;
1446e5451bcdSMatthias Ringwald     }
1447e5451bcdSMatthias Ringwald }
1448e5451bcdSMatthias Ringwald 
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)14496bcfb631SMilanka 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){
1450cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1451cf26c8fbSMilanka Ringwald 
1452cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1453cf26c8fbSMilanka Ringwald     if (client != NULL){
1454cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1455cf26c8fbSMilanka Ringwald     }
1456cf26c8fbSMilanka Ringwald 
1457cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1458cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1459cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1460cf26c8fbSMilanka Ringwald     }
1461cf26c8fbSMilanka Ringwald 
1462cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1463cf26c8fbSMilanka Ringwald     if (client == NULL) {
1464cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1465cf26c8fbSMilanka Ringwald     }
1466cf26c8fbSMilanka Ringwald 
14676bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1468cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1469cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1470cf26c8fbSMilanka Ringwald 
1471cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1472cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1473cf26c8fbSMilanka Ringwald }
1474cf26c8fbSMilanka Ringwald 
hids_client_disconnect(uint16_t hids_cid)1475cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1476cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1477cf26c8fbSMilanka Ringwald     if (client == NULL){
1478cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1479cf26c8fbSMilanka Ringwald     }
1480cf26c8fbSMilanka Ringwald     // finalize connection
1481cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1482cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1483cf26c8fbSMilanka Ringwald }
1484cf26c8fbSMilanka Ringwald 
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)1485fd39e93aSMilanka 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){
14861624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
14871624214bSMilanka Ringwald     if (client == NULL){
14881624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14891624214bSMilanka Ringwald     }
14901624214bSMilanka Ringwald 
14911624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
14921624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14931624214bSMilanka Ringwald     }
14941624214bSMilanka Ringwald 
1495fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
149683d7ed1cSMilanka Ringwald 
14971624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
14981624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
14991624214bSMilanka Ringwald     }
15001624214bSMilanka Ringwald 
15011624214bSMilanka Ringwald     uint16_t mtu;
15021624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
15031624214bSMilanka Ringwald 
15041624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
15051624214bSMilanka Ringwald         return status;
15061624214bSMilanka Ringwald     }
15071624214bSMilanka Ringwald 
15081624214bSMilanka Ringwald     if (mtu - 2 < report_len){
15091624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
15101624214bSMilanka Ringwald     }
15111624214bSMilanka Ringwald 
15126d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1513021192e1SMilanka Ringwald     client->report_index = report_index;
15141624214bSMilanka Ringwald     client->report = report;
15151624214bSMilanka Ringwald     client->report_len = report_len;
15161624214bSMilanka Ringwald 
15171624214bSMilanka Ringwald     hids_run_for_client(client);
15181624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
15191624214bSMilanka Ringwald }
15201624214bSMilanka Ringwald 
hids_client_send_get_report(uint16_t hids_cid,uint8_t report_id,hid_report_type_t report_type)1521fd39e93aSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
152283d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
152383d7ed1cSMilanka Ringwald     if (client == NULL){
152483d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
152583d7ed1cSMilanka Ringwald     }
152683d7ed1cSMilanka Ringwald 
152783d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
152883d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
152983d7ed1cSMilanka Ringwald     }
153083d7ed1cSMilanka Ringwald 
1531fd39e93aSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
153283d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
153383d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
153483d7ed1cSMilanka Ringwald     }
153583d7ed1cSMilanka Ringwald 
153683d7ed1cSMilanka Ringwald     client->report_index = report_index;
153783d7ed1cSMilanka Ringwald 
153883d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
153983d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
154083d7ed1cSMilanka Ringwald #else
154183d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
154283d7ed1cSMilanka Ringwald #endif
154383d7ed1cSMilanka Ringwald     hids_run_for_client(client);
154483d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
154583d7ed1cSMilanka Ringwald }
154683d7ed1cSMilanka Ringwald 
154783d7ed1cSMilanka Ringwald 
hids_client_get_hid_information(uint16_t hids_cid,uint8_t service_index)1548f4d3b82aSMilanka Ringwald uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1549f4d3b82aSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1550f4d3b82aSMilanka Ringwald     if (client == NULL){
1551f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1552f4d3b82aSMilanka Ringwald     }
1553f4d3b82aSMilanka Ringwald 
1554f4d3b82aSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1555f4d3b82aSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1556f4d3b82aSMilanka Ringwald     }
1557f4d3b82aSMilanka Ringwald 
1558f4d3b82aSMilanka Ringwald     if (service_index >= client->num_instances){
1559f4d3b82aSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1560f4d3b82aSMilanka Ringwald     }
1561f4d3b82aSMilanka Ringwald 
1562f4d3b82aSMilanka Ringwald     client->service_index = service_index;
1563af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].hid_information_value_handle;
1564af2241c2SMilanka Ringwald 
1565af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1566af2241c2SMilanka Ringwald     hids_run_for_client(client);
1567af2241c2SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1568af2241c2SMilanka Ringwald }
1569af2241c2SMilanka Ringwald 
hids_client_get_protocol_mode(uint16_t hids_cid,uint8_t service_index)1570af2241c2SMilanka Ringwald uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1571af2241c2SMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1572af2241c2SMilanka Ringwald     if (client == NULL){
1573af2241c2SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1574af2241c2SMilanka Ringwald     }
1575af2241c2SMilanka Ringwald 
1576af2241c2SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1577af2241c2SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1578af2241c2SMilanka Ringwald     }
1579af2241c2SMilanka Ringwald 
1580af2241c2SMilanka Ringwald     if (service_index >= client->num_instances){
1581af2241c2SMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1582af2241c2SMilanka Ringwald     }
1583af2241c2SMilanka Ringwald 
1584af2241c2SMilanka Ringwald     client->service_index = service_index;
1585af2241c2SMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1586af2241c2SMilanka Ringwald 
1587af2241c2SMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1588f4d3b82aSMilanka Ringwald     hids_run_for_client(client);
1589f4d3b82aSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1590f4d3b82aSMilanka Ringwald }
1591f4d3b82aSMilanka Ringwald 
hids_client_send_set_protocol_mode(uint16_t hids_cid,uint8_t service_index,hid_protocol_mode_t protocol_mode)15920cbdb21bSMilanka Ringwald uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
15936d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
15946d6f7efcSMilanka Ringwald     if (client == NULL){
15956d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
15966d6f7efcSMilanka Ringwald     }
15976d6f7efcSMilanka Ringwald 
15986d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
15996d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
16006d6f7efcSMilanka Ringwald     }
16016d6f7efcSMilanka Ringwald 
16026d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
16036d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
16046d6f7efcSMilanka Ringwald     }
16056d6f7efcSMilanka Ringwald 
16066d6f7efcSMilanka Ringwald     client->service_index = service_index;
16076d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].protocol_mode_value_handle;
16086d6f7efcSMilanka Ringwald     client->value = (uint8_t)protocol_mode;
16096d6f7efcSMilanka Ringwald 
16106d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
16116d6f7efcSMilanka Ringwald     hids_run_for_client(client);
16126d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
16136d6f7efcSMilanka Ringwald }
16146d6f7efcSMilanka Ringwald 
16156d6f7efcSMilanka Ringwald 
hids_client_send_control_point_cmd(uint16_t hids_cid,uint8_t service_index,uint8_t value)16166d6f7efcSMilanka Ringwald static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
16176d6f7efcSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
16186d6f7efcSMilanka Ringwald     if (client == NULL){
16196d6f7efcSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
16206d6f7efcSMilanka Ringwald     }
16216d6f7efcSMilanka Ringwald 
16226d6f7efcSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
16236d6f7efcSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
16246d6f7efcSMilanka Ringwald     }
16256d6f7efcSMilanka Ringwald 
16266d6f7efcSMilanka Ringwald     if (service_index >= client->num_instances){
16276d6f7efcSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
16286d6f7efcSMilanka Ringwald     }
16296d6f7efcSMilanka Ringwald 
16306d6f7efcSMilanka Ringwald     client->service_index = service_index;
16316d6f7efcSMilanka Ringwald     client->handle = client->services[client->service_index].control_point_value_handle;
16326d6f7efcSMilanka Ringwald     client->value = value;
16336d6f7efcSMilanka Ringwald 
16346d6f7efcSMilanka Ringwald     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
16356d6f7efcSMilanka Ringwald     hids_run_for_client(client);
16366d6f7efcSMilanka Ringwald     return ERROR_CODE_SUCCESS;
16376d6f7efcSMilanka Ringwald }
16386d6f7efcSMilanka Ringwald 
hids_client_send_suspend(uint16_t hids_cid,uint8_t service_index)16396d6f7efcSMilanka Ringwald uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
16406d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
16416d6f7efcSMilanka Ringwald }
16426d6f7efcSMilanka Ringwald 
hids_client_send_exit_suspend(uint16_t hids_cid,uint8_t service_index)16436d6f7efcSMilanka Ringwald uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
16446d6f7efcSMilanka Ringwald     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
16456d6f7efcSMilanka Ringwald }
16466d6f7efcSMilanka Ringwald 
hids_client_enable_notifications(uint16_t hids_cid)16470cbdb21bSMilanka Ringwald uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1648cd28e7b1SMilanka Ringwald      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1649cd28e7b1SMilanka Ringwald     if (client == NULL){
1650cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1651cd28e7b1SMilanka Ringwald     }
1652cd28e7b1SMilanka Ringwald 
1653cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1654cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1655cd28e7b1SMilanka Ringwald     }
1656cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1657cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1658cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1659cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1660cd28e7b1SMilanka Ringwald     }
1661cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1662cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1663cd28e7b1SMilanka Ringwald }
1664cd28e7b1SMilanka Ringwald 
hids_client_disable_notifications(uint16_t hids_cid)16650cbdb21bSMilanka Ringwald uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1666cd28e7b1SMilanka Ringwald          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1667cd28e7b1SMilanka Ringwald     if (client == NULL){
1668cd28e7b1SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1669cd28e7b1SMilanka Ringwald     }
1670cd28e7b1SMilanka Ringwald 
1671cd28e7b1SMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1672cd28e7b1SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1673cd28e7b1SMilanka Ringwald     }
1674cd28e7b1SMilanka Ringwald 
1675cd28e7b1SMilanka Ringwald     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1676cd28e7b1SMilanka Ringwald     if (hids_client_notifications_configuration_init(client)){
1677cd28e7b1SMilanka Ringwald         hids_run_for_client(client);
1678cd28e7b1SMilanka Ringwald         return ERROR_CODE_SUCCESS;
1679cd28e7b1SMilanka Ringwald     }
1680cd28e7b1SMilanka Ringwald     hids_emit_notifications_configuration(client);
1681cd28e7b1SMilanka Ringwald     return ERROR_CODE_SUCCESS;
1682cd28e7b1SMilanka Ringwald }
16836d6f7efcSMilanka Ringwald 
hids_client_init(uint8_t * hid_descriptor_storage,uint16_t hid_descriptor_storage_len)1684021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1685021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1686021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1687e5451bcdSMatthias Ringwald 
1688e5451bcdSMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
1689e5451bcdSMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
1690021192e1SMilanka Ringwald }
1691cf26c8fbSMilanka Ringwald 
hids_client_deinit(void)1692cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1693