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