xref: /btstack/src/ble/gatt-service/hids_client.c (revision 6bcfb6310da3267466803219d26139f49ab94cf4)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23fc975d0eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24fc975d0eSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42fc975d0eSMilanka Ringwald #include <stdint.h>
43fc975d0eSMilanka Ringwald #include <string.h>
44fc975d0eSMilanka Ringwald 
45cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
46fc975d0eSMilanka Ringwald 
47cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
48fc975d0eSMilanka Ringwald #include "ble/att_db.h"
49fc975d0eSMilanka Ringwald #include "ble/core.h"
50fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
51fc975d0eSMilanka Ringwald #include "ble/sm.h"
52cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
53fc975d0eSMilanka Ringwald #include "btstack_debug.h"
54fc975d0eSMilanka Ringwald #include "btstack_event.h"
55fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
56fc975d0eSMilanka Ringwald #include "gap.h"
57fc975d0eSMilanka Ringwald 
58cf26c8fbSMilanka Ringwald static btstack_linked_list_t clients;
59cf26c8fbSMilanka Ringwald static uint16_t hids_cid_counter = 0;
60fc975d0eSMilanka Ringwald 
61cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
62cf26c8fbSMilanka Ringwald 
63cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
64cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
65cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
66cf26c8fbSMilanka Ringwald     } else {
67cf26c8fbSMilanka Ringwald         hids_cid_counter++;
68cf26c8fbSMilanka Ringwald     }
69cf26c8fbSMilanka Ringwald     return hids_cid_counter;
70fc975d0eSMilanka Ringwald }
71fc975d0eSMilanka Ringwald 
72cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
73cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
74cf26c8fbSMilanka Ringwald     if (!client){
75cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
76cf26c8fbSMilanka Ringwald         return NULL;
77cf26c8fbSMilanka Ringwald     }
78cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
79cf26c8fbSMilanka Ringwald     client->cid = cid;
80cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
81fc975d0eSMilanka Ringwald 
82cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
83cf26c8fbSMilanka Ringwald     return client;
84fc975d0eSMilanka Ringwald }
85fc975d0eSMilanka Ringwald 
86cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
87cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
88cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
89fc975d0eSMilanka Ringwald }
90cf26c8fbSMilanka Ringwald 
91cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
92cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
93cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
94cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
95cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
96cf26c8fbSMilanka Ringwald         if (client->con_handle != con_handle) continue;
97cf26c8fbSMilanka Ringwald         return client;
98cf26c8fbSMilanka Ringwald     }
99cf26c8fbSMilanka Ringwald     return NULL;
100cf26c8fbSMilanka Ringwald }
101cf26c8fbSMilanka Ringwald 
102cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
103cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
104cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
105cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
106cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
107cf26c8fbSMilanka Ringwald         if (client->cid != hids_cid) continue;
108cf26c8fbSMilanka Ringwald         return client;
109cf26c8fbSMilanka Ringwald     }
110cf26c8fbSMilanka Ringwald     return NULL;
111cf26c8fbSMilanka Ringwald }
112cf26c8fbSMilanka Ringwald 
113cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
114*6bcfb631SMilanka Ringwald     uint8_t event[8];
115cf26c8fbSMilanka Ringwald     int pos = 0;
116cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
117cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
118cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
119cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
120cf26c8fbSMilanka Ringwald     pos += 2;
121cf26c8fbSMilanka Ringwald     event[pos++] = status;
122*6bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
123cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
124cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
125cf26c8fbSMilanka Ringwald }
126cf26c8fbSMilanka Ringwald 
127cf26c8fbSMilanka Ringwald 
128cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
129cf26c8fbSMilanka Ringwald     uint8_t att_status;
130*6bcfb631SMilanka Ringwald     gatt_client_service_t service;
131*6bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
132cf26c8fbSMilanka Ringwald 
133cf26c8fbSMilanka Ringwald     switch (client->state){
134cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
135cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
136cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
137cf26c8fbSMilanka Ringwald             // TODO handle status
138cf26c8fbSMilanka Ringwald             UNUSED(att_status);
139cf26c8fbSMilanka Ringwald             break;
140cf26c8fbSMilanka Ringwald 
141cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
142*6bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
143*6bcfb631SMilanka Ringwald 
144*6bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
145*6bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
146*6bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
147*6bcfb631SMilanka Ringwald 
148*6bcfb631SMilanka Ringwald             UNUSED(att_status);
149*6bcfb631SMilanka Ringwald             break;
150*6bcfb631SMilanka Ringwald 
151*6bcfb631SMilanka Ringwald         case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
152*6bcfb631SMilanka Ringwald             characteristic.value_handle = client->boot_keyboard_input_value_handle;
153*6bcfb631SMilanka Ringwald             characteristic.end_handle = client->boot_keyboard_input_end_handle;
154*6bcfb631SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
155*6bcfb631SMilanka Ringwald             UNUSED(att_status);
156*6bcfb631SMilanka Ringwald             break;
157*6bcfb631SMilanka Ringwald 
158*6bcfb631SMilanka Ringwald         case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
159*6bcfb631SMilanka Ringwald             characteristic.value_handle = client->boot_mouse_input_value_handle;
160*6bcfb631SMilanka Ringwald             characteristic.end_handle = client->boot_mouse_input_end_handle;
161*6bcfb631SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
162*6bcfb631SMilanka Ringwald             UNUSED(att_status);
163*6bcfb631SMilanka Ringwald             break;
164*6bcfb631SMilanka Ringwald 
165*6bcfb631SMilanka Ringwald         case HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE:
166*6bcfb631SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
167*6bcfb631SMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->protocol_mode);
168*6bcfb631SMilanka Ringwald             UNUSED(att_status);
169*6bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
170*6bcfb631SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
171cf26c8fbSMilanka Ringwald             break;
172cf26c8fbSMilanka Ringwald 
173cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_CONNECTED:
174*6bcfb631SMilanka Ringwald 
175*6bcfb631SMilanka Ringwald             break;
176*6bcfb631SMilanka Ringwald 
177*6bcfb631SMilanka Ringwald         default:
178*6bcfb631SMilanka Ringwald             break;
179*6bcfb631SMilanka Ringwald     }
180*6bcfb631SMilanka Ringwald }
181*6bcfb631SMilanka Ringwald 
182*6bcfb631SMilanka Ringwald static hid_service_client_state_t boot_protocol_mode_setup_next_state(hids_client_t * client){
183*6bcfb631SMilanka Ringwald     hid_service_client_state_t state = HIDS_CLIENT_STATE_IDLE;
184*6bcfb631SMilanka Ringwald 
185*6bcfb631SMilanka Ringwald     switch (client->state){
186*6bcfb631SMilanka Ringwald         case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
187*6bcfb631SMilanka Ringwald             // set protocol mode
188*6bcfb631SMilanka Ringwald             if (client->boot_keyboard_input_value_handle != 0){
189*6bcfb631SMilanka Ringwald                 state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
190*6bcfb631SMilanka Ringwald                 break;
191*6bcfb631SMilanka Ringwald             }
192*6bcfb631SMilanka Ringwald             if (client->boot_mouse_input_value_handle != 0){
193*6bcfb631SMilanka Ringwald                 state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
194*6bcfb631SMilanka Ringwald                 break;
195*6bcfb631SMilanka Ringwald             }
196*6bcfb631SMilanka Ringwald             break;
197*6bcfb631SMilanka Ringwald         case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
198*6bcfb631SMilanka Ringwald             if (client->boot_mouse_input_value_handle != 0){
199*6bcfb631SMilanka Ringwald                 state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
200*6bcfb631SMilanka Ringwald                 break;
201*6bcfb631SMilanka Ringwald             }
202cf26c8fbSMilanka Ringwald             break;
203cf26c8fbSMilanka Ringwald         default:
204cf26c8fbSMilanka Ringwald             break;
205cf26c8fbSMilanka Ringwald     }
206*6bcfb631SMilanka Ringwald     return state;
207*6bcfb631SMilanka Ringwald }
208*6bcfb631SMilanka Ringwald 
209*6bcfb631SMilanka Ringwald static void handle_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
210*6bcfb631SMilanka Ringwald     UNUSED(packet_type);
211*6bcfb631SMilanka Ringwald     UNUSED(channel);
212*6bcfb631SMilanka Ringwald     UNUSED(size);
213*6bcfb631SMilanka Ringwald     // sent HID_SUBEVENT_REPORT similar event
214cf26c8fbSMilanka Ringwald }
215cf26c8fbSMilanka Ringwald 
216cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
217cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
218cf26c8fbSMilanka Ringwald     UNUSED(channel);
219cf26c8fbSMilanka Ringwald     UNUSED(size);
220cf26c8fbSMilanka Ringwald 
221*6bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
222cf26c8fbSMilanka Ringwald     uint8_t att_status;
223*6bcfb631SMilanka Ringwald     gatt_client_service_t service;
224*6bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
225cf26c8fbSMilanka Ringwald 
226cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
227cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
228cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
229cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
230cf26c8fbSMilanka Ringwald 
231*6bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
232*6bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
233*6bcfb631SMilanka Ringwald                 hids_finalize_client(client);
234*6bcfb631SMilanka Ringwald                 break;
235*6bcfb631SMilanka Ringwald             }
236*6bcfb631SMilanka Ringwald 
237*6bcfb631SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
238*6bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
239*6bcfb631SMilanka Ringwald                 client->services[client->num_instances].start_handle = service.start_group_handle;
240*6bcfb631SMilanka Ringwald                 client->services[client->num_instances].end_handle = service.end_group_handle;
241*6bcfb631SMilanka Ringwald             }
242*6bcfb631SMilanka Ringwald             client->num_instances++;
243*6bcfb631SMilanka Ringwald             break;
244*6bcfb631SMilanka Ringwald 
245*6bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
246*6bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
247*6bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
248*6bcfb631SMilanka Ringwald 
249*6bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
250*6bcfb631SMilanka Ringwald             switch (characteristic.uuid16){
251*6bcfb631SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
252*6bcfb631SMilanka Ringwald                     client->boot_keyboard_input_value_handle = characteristic.value_handle;
253*6bcfb631SMilanka Ringwald                     break;
254*6bcfb631SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
255*6bcfb631SMilanka Ringwald                     client->boot_mouse_input_value_handle = characteristic.value_handle;
256*6bcfb631SMilanka Ringwald                     break;
257*6bcfb631SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
258*6bcfb631SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
259*6bcfb631SMilanka Ringwald                     break;
260*6bcfb631SMilanka Ringwald                 default:
261*6bcfb631SMilanka Ringwald                     break;
262*6bcfb631SMilanka Ringwald             }
263cf26c8fbSMilanka Ringwald             break;
264cf26c8fbSMilanka Ringwald 
265cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
266cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
267cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
268cf26c8fbSMilanka Ringwald 
269cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
270cf26c8fbSMilanka Ringwald 
271cf26c8fbSMilanka Ringwald             switch (client->state){
272cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
273*6bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
274*6bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
275*6bcfb631SMilanka Ringwald                         hids_finalize_client(client);
276cf26c8fbSMilanka Ringwald                         break;
277*6bcfb631SMilanka Ringwald                     }
278*6bcfb631SMilanka Ringwald 
279*6bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
280*6bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
281*6bcfb631SMilanka Ringwald                         hids_finalize_client(client);
282*6bcfb631SMilanka Ringwald                         break;
283*6bcfb631SMilanka Ringwald                     }
284*6bcfb631SMilanka Ringwald 
285*6bcfb631SMilanka Ringwald                     if (client->num_instances > MAX_NUM_HID_SERVICES) {
286*6bcfb631SMilanka Ringwald                         log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES);
287*6bcfb631SMilanka Ringwald                         client->num_instances = MAX_NUM_HID_SERVICES;
288*6bcfb631SMilanka Ringwald                     }
289*6bcfb631SMilanka Ringwald 
290*6bcfb631SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
291*6bcfb631SMilanka Ringwald                     client->service_index = 0;
292*6bcfb631SMilanka Ringwald                     break;
293*6bcfb631SMilanka Ringwald 
294*6bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
295*6bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
296*6bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
297*6bcfb631SMilanka Ringwald                         hids_finalize_client(client);
298*6bcfb631SMilanka Ringwald                         break;
299*6bcfb631SMilanka Ringwald                     }
300*6bcfb631SMilanka Ringwald 
301*6bcfb631SMilanka Ringwald                     switch (client->required_protocol_mode){
302*6bcfb631SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
303*6bcfb631SMilanka Ringwald                             if (client->boot_keyboard_input_value_handle != 0){
304*6bcfb631SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
305*6bcfb631SMilanka Ringwald                                 break;
306*6bcfb631SMilanka Ringwald                             }
307*6bcfb631SMilanka Ringwald                             if (client->boot_mouse_input_value_handle != 0){
308*6bcfb631SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
309*6bcfb631SMilanka Ringwald                                 break;
310*6bcfb631SMilanka Ringwald                             }
311*6bcfb631SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
312*6bcfb631SMilanka Ringwald                             hids_finalize_client(client);
313*6bcfb631SMilanka Ringwald                             break;
314*6bcfb631SMilanka Ringwald                         default:
315*6bcfb631SMilanka Ringwald                             break;
316*6bcfb631SMilanka Ringwald                     }
317*6bcfb631SMilanka Ringwald                     break;
318*6bcfb631SMilanka Ringwald 
319*6bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
320*6bcfb631SMilanka Ringwald                     // setup listener
321*6bcfb631SMilanka Ringwald                     characteristic.value_handle = client->boot_keyboard_input_value_handle;
322*6bcfb631SMilanka Ringwald                     gatt_client_listen_for_characteristic_value_updates(&client->boot_keyboard_notifications, &handle_hid_event, client->con_handle, &characteristic);
323*6bcfb631SMilanka Ringwald 
324*6bcfb631SMilanka Ringwald                     if (client->boot_mouse_input_value_handle != 0){
325*6bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
326*6bcfb631SMilanka Ringwald                         break;
327*6bcfb631SMilanka Ringwald                     }
328*6bcfb631SMilanka Ringwald                     // set protocol
329*6bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
330*6bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
331*6bcfb631SMilanka Ringwald                     } else {
332*6bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
333*6bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
334*6bcfb631SMilanka Ringwald                     }
335*6bcfb631SMilanka Ringwald                     break;
336*6bcfb631SMilanka Ringwald 
337*6bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
338*6bcfb631SMilanka Ringwald                     // setup listener
339*6bcfb631SMilanka Ringwald                     characteristic.value_handle = client->boot_mouse_input_value_handle;
340*6bcfb631SMilanka Ringwald                     gatt_client_listen_for_characteristic_value_updates(&client->boot_mouse_notifications, &handle_hid_event, client->con_handle, &characteristic);
341*6bcfb631SMilanka Ringwald 
342*6bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
343*6bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
344*6bcfb631SMilanka Ringwald                     } else {
345*6bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
346*6bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
347*6bcfb631SMilanka Ringwald                     }
348*6bcfb631SMilanka Ringwald                     break;
349*6bcfb631SMilanka Ringwald 
350*6bcfb631SMilanka Ringwald 
351cf26c8fbSMilanka Ringwald                 default:
352cf26c8fbSMilanka Ringwald                     break;
353cf26c8fbSMilanka Ringwald             }
354cf26c8fbSMilanka Ringwald             break;
355cf26c8fbSMilanka Ringwald 
356cf26c8fbSMilanka Ringwald         default:
357cf26c8fbSMilanka Ringwald             break;
358cf26c8fbSMilanka Ringwald     }
359*6bcfb631SMilanka Ringwald 
360*6bcfb631SMilanka Ringwald     if (client != NULL){
361*6bcfb631SMilanka Ringwald         hids_run_for_client(client);
362*6bcfb631SMilanka Ringwald     }
363cf26c8fbSMilanka Ringwald }
364cf26c8fbSMilanka Ringwald 
365*6bcfb631SMilanka 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){
366cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
367cf26c8fbSMilanka Ringwald 
368cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
369cf26c8fbSMilanka Ringwald     if (client != NULL){
370cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
371cf26c8fbSMilanka Ringwald     }
372cf26c8fbSMilanka Ringwald 
373cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
374cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
375cf26c8fbSMilanka Ringwald         *hids_cid = cid;
376cf26c8fbSMilanka Ringwald     }
377cf26c8fbSMilanka Ringwald 
378cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
379cf26c8fbSMilanka Ringwald     if (client == NULL) {
380cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
381cf26c8fbSMilanka Ringwald     }
382cf26c8fbSMilanka Ringwald 
383*6bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
384cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
385cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
386cf26c8fbSMilanka Ringwald 
387cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
388cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
389cf26c8fbSMilanka Ringwald }
390cf26c8fbSMilanka Ringwald 
391cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
392cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
393cf26c8fbSMilanka Ringwald     if (client == NULL){
394cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
395cf26c8fbSMilanka Ringwald     }
396cf26c8fbSMilanka Ringwald     // finalize connection
397cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
398cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
399cf26c8fbSMilanka Ringwald }
400cf26c8fbSMilanka Ringwald 
401cf26c8fbSMilanka Ringwald void hids_client_init(void){}
402cf26c8fbSMilanka Ringwald 
403cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
404