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