xref: /btstack/src/ble/gatt-service/scan_parameters_service_client.c (revision d72e9d4b1d8cea3c48e9897368c446f554491bbf)
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 BLUEKITCHEN
24  * GMBH 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__ "scan_parameters_service_client.c"
39 
40 #include "btstack_config.h"
41 
42 #include <stdint.h>
43 #include <string.h>
44 
45 #ifdef ENABLE_TESTING_SUPPORT
46 #include <stdio.h>
47 #endif
48 
49 #include "scan_parameters_service_client.h"
50 
51 #include "btstack_memory.h"
52 #include "ble/core.h"
53 #include "ble/gatt_client.h"
54 #include "bluetooth_gatt.h"
55 #include "btstack_debug.h"
56 #include "btstack_event.h"
57 #include "btstack_run_loop.h"
58 #include "gap.h"
59 
60 static btstack_packet_callback_registration_t hci_event_callback_registration;
61 
62 static btstack_linked_list_t clients;
63 static uint16_t scan_parameters_service_cid_counter = 0;
64 static uint16_t scan_parameters_service_scan_window = 0;
65 static uint16_t scan_parameters_service_scan_interval = 0;
66 
67 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
68 static void scan_parameters_service_run_for_client(scan_parameters_service_client_t * client);
69 
70 static uint16_t scan_parameters_service_get_next_cid(void){
71     if (scan_parameters_service_cid_counter == 0xffff) {
72         scan_parameters_service_cid_counter = 1;
73     } else {
74         scan_parameters_service_cid_counter++;
75     }
76     return scan_parameters_service_cid_counter;
77 }
78 
79 static scan_parameters_service_client_t * scan_parameters_service_create_client(hci_con_handle_t con_handle, uint16_t cid){
80     scan_parameters_service_client_t * client = btstack_memory_scan_parameters_service_client_get();
81     if (!client){
82         log_error("Not enough memory to create client");
83         return NULL;
84     }
85 
86     client->cid = cid;
87     client->con_handle = con_handle;
88     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_IDLE;
89 
90     client->start_handle = 0;
91     client->end_handle = 0;
92 
93     client->scan_interval_window_value_handle = 0;
94     client->scan_interval_window_value_update = false;
95     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
96     return client;
97 }
98 
99 static void scan_parameters_service_finalize_client(scan_parameters_service_client_t * client){
100     gatt_client_stop_listening_for_characteristic_value_updates(&client->notification_listener);
101     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
102     btstack_memory_scan_parameters_service_client_free(client);
103 }
104 
105 static scan_parameters_service_client_t * scan_parameters_service_get_client_for_con_handle(hci_con_handle_t con_handle){
106     btstack_linked_list_iterator_t it;
107     btstack_linked_list_iterator_init(&it, &clients);
108     while (btstack_linked_list_iterator_has_next(&it)){
109         scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it);
110         if (client->con_handle != con_handle) continue;
111         return client;
112     }
113     return NULL;
114 }
115 
116 static scan_parameters_service_client_t * scan_parameters_service_get_client_for_cid(uint16_t scan_parameters_service_cid){
117     btstack_linked_list_iterator_t it;
118     btstack_linked_list_iterator_init(&it, &clients);
119     while (btstack_linked_list_iterator_has_next(&it)){
120         scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it);
121         if (client->cid != scan_parameters_service_cid) continue;
122         return client;
123     }
124     return NULL;
125 }
126 
127 static void scan_parameters_service_emit_connection_established(scan_parameters_service_client_t * client, uint8_t status){
128     uint8_t event[6];
129     int pos = 0;
130     event[pos++] = HCI_EVENT_GATTSERVICE_META;
131     event[pos++] = sizeof(event) - 2;
132     event[pos++] = GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_CONNECTED;
133     little_endian_store_16(event, pos, client->cid);
134     pos += 2;
135     event[pos++] = status;
136     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, 0, event, sizeof(event));
137 }
138 
139 static void scan_parameters_service_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){
140     uint8_t event[5];
141     int pos = 0;
142     event[pos++] = HCI_EVENT_GATTSERVICE_META;
143     event[pos++] = sizeof(event) - 2;
144     event[pos++] = GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_DISCONNECTED;
145     little_endian_store_16(event, pos, cid);
146     pos += 2;
147     btstack_assert(pos == sizeof(event));
148     (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
149 }
150 
151 static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
152     UNUSED(packet_type);
153     UNUSED(channel);
154     UNUSED(size);
155 
156     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
157 
158     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
159     btstack_assert(client != NULL);
160     client->scan_interval_window_value_update = true;
161     scan_parameters_service_run_for_client(client);
162 }
163 
164 static void scan_parameters_service_run_for_client(scan_parameters_service_client_t * client){
165     uint8_t att_status;
166     gatt_client_service_t service;
167 
168     gatt_client_characteristic_t characteristic;
169 
170     switch (client->state){
171         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
172 #ifdef ENABLE_TESTING_SUPPORT
173             printf("\n\nQuery Services:\n");
174 #endif
175             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
176             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_SCAN_PARAMETERS);
177             // TODO handle status
178             UNUSED(att_status);
179             break;
180 
181         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
182 #ifdef ENABLE_TESTING_SUPPORT
183             printf("\n\nQuery Characteristics of service\n");
184 #endif
185             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
186             service.start_group_handle = client->start_handle;
187             service.end_group_handle = client->end_handle;
188             att_status = gatt_client_discover_characteristics_for_service(
189                 handle_gatt_client_event, client->con_handle, &service);
190             // TODO handle status
191             UNUSED(att_status);
192             break;
193 
194         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED:
195             if (client->scan_interval_window_value_update){
196                 client->scan_interval_window_value_update = false;
197 
198 #ifdef ENABLE_TESTING_SUPPORT
199                 printf("\n\nUpdate - interval %d, window %d:\n", scan_parameters_service_scan_interval, scan_parameters_service_scan_window);
200 #endif
201                 uint8_t value[4];
202                 little_endian_store_16(value, 0, scan_parameters_service_scan_interval);
203                 little_endian_store_16(value, 2, scan_parameters_service_scan_window);
204 
205                 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->scan_interval_window_value_handle, 4, value);
206                 // TODO handle status
207                 UNUSED(att_status);
208             }
209             break;
210 #ifdef ENABLE_TESTING_SUPPORT
211         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC:
212             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC;
213 
214             characteristic.value_handle = client->scan_refresh_value_handle;
215             characteristic.end_handle   =  client->scan_refresh_end_handle;
216 
217             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
218             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
219             UNUSED(att_status);
220             break;
221 #endif
222         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
223 #ifdef ENABLE_TESTING_SUPPORT
224             printf("    Notification configuration enable ");
225 #endif
226 
227             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
228 
229             characteristic.value_handle = client->scan_refresh_value_handle;
230             characteristic.end_handle = client->scan_refresh_end_handle;
231             characteristic.properties = client->scan_refresh_properties;
232 
233             // end of write marked in GATT_EVENT_QUERY_COMPLETE
234 
235             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
236 
237             if (att_status == ERROR_CODE_SUCCESS){
238                 gatt_client_listen_for_characteristic_value_updates(
239                             &client->notification_listener,
240                             &handle_notification_event, client->con_handle, &characteristic);
241             }
242             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
243             break;
244         default:
245             break;
246     }
247 }
248 
249 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
250     UNUSED(packet_type);
251     UNUSED(channel);
252     UNUSED(size);
253 
254     scan_parameters_service_client_t * client = NULL;
255     gatt_client_service_t service;
256     gatt_client_characteristic_t characteristic;
257     uint8_t att_status;
258 
259 #ifdef ENABLE_TESTING_SUPPORT
260     gatt_client_characteristic_descriptor_t characteristic_descriptor;
261 #endif
262 
263     switch(hci_event_packet_get_type(packet)){
264         case GATT_EVENT_SERVICE_QUERY_RESULT:
265             client = scan_parameters_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
266             btstack_assert(client != NULL);
267 
268             if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) {
269                 scan_parameters_service_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
270                 scan_parameters_service_finalize_client(client);
271                 return;
272             }
273 
274             gatt_event_service_query_result_get_service(packet, &service);
275             client->start_handle = service.start_group_handle;
276             client->end_handle = service.end_group_handle;
277 #ifdef ENABLE_TESTING_SUPPORT
278             printf("ScS Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle);
279 #endif
280             break;
281 
282         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
283             client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
284             btstack_assert(client != NULL);
285 
286             // found scan_interval_window_value_handle, check att_status
287             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
288             switch (characteristic.uuid16){
289                 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_INTERVAL_WINDOW:
290                     client->scan_interval_window_value_handle = characteristic.value_handle;
291 
292 #ifdef ENABLE_TESTING_SUPPORT
293                     printf("ScS Scan Interval Characteristic:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
294                         characteristic.start_handle,
295                         characteristic.properties,
296                         characteristic.value_handle, characteristic.uuid16);
297 #endif
298                     break;
299                 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_REFRESH:
300                     client->scan_refresh_value_handle = characteristic.value_handle;
301                     client->scan_refresh_end_handle = characteristic.end_handle;
302                     client->scan_refresh_properties = characteristic.properties;
303 
304 #ifdef ENABLE_TESTING_SUPPORT
305                     printf("ScS Scan Refresh Characteristic:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
306                         characteristic.start_handle,
307                         characteristic.properties,
308                         characteristic.value_handle, characteristic.uuid16);
309 #endif
310                     break;
311                 default:
312                     break;
313             }
314             break;
315 
316 #ifdef ENABLE_TESTING_SUPPORT
317         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
318             client = scan_parameters_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
319             btstack_assert(client != NULL);
320 
321             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
322             if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
323                 printf("    Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
324                     characteristic_descriptor.handle,
325                     characteristic_descriptor.uuid16);
326             }
327             break;
328 
329         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
330             client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
331             btstack_assert(client != NULL);
332 
333             printf("    Received CCC value: ");
334             printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet),  gatt_event_characteristic_value_query_result_get_value_length(packet));
335             break;
336 #endif
337 
338         case GATT_EVENT_QUERY_COMPLETE:
339             client = scan_parameters_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
340             btstack_assert(client != NULL);
341 
342             att_status = gatt_event_query_complete_get_att_status(packet);
343 
344             switch (client->state){
345                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
346                     if (att_status != ATT_ERROR_SUCCESS){
347                         scan_parameters_service_emit_connection_established(client, att_status);
348                         scan_parameters_service_finalize_client(client);
349                         return;
350                     }
351 
352                     if (client->start_handle != 0){
353                         client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
354                         break;
355                     }
356 
357                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
358                     scan_parameters_service_finalize_client(client);
359                     return;
360 
361                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
362                     if (att_status != ATT_ERROR_SUCCESS){
363                         scan_parameters_service_emit_connection_established(client, att_status);
364                         scan_parameters_service_finalize_client(client);
365                         break;
366                     }
367                     if (client->scan_interval_window_value_handle == 0){
368                         scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
369                         scan_parameters_service_finalize_client(client);
370                         return;
371                     }
372 #ifdef ENABLE_TESTING_SUPPORT
373                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC;
374 #else
375                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
376                     client->scan_interval_window_value_update = true;
377                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
378 #endif
379                     break;
380 
381 #ifdef ENABLE_TESTING_SUPPORT
382                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC:
383                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
384                     client->scan_interval_window_value_update = true;
385                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
386                     break;
387 #endif
388                 default:
389                     break;
390             }
391             break;
392         default:
393             break;
394     }
395 
396     if (client != NULL){
397         scan_parameters_service_run_for_client(client);
398     }
399 }
400 
401 static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
402     UNUSED(packet_type); // ok: only hci events
403     UNUSED(channel);     // ok: there is no channel
404     UNUSED(size);        // ok: fixed format events read from HCI buffer
405 
406     hci_con_handle_t con_handle;
407     scan_parameters_service_client_t * client;
408 
409     switch (hci_event_packet_get_type(packet)) {
410         case HCI_EVENT_DISCONNECTION_COMPLETE:
411             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
412             client = scan_parameters_service_get_client_for_con_handle(con_handle);
413             if (client != NULL){
414                 // emit disconnected
415                 btstack_packet_handler_t packet_handler = client->client_handler;
416                 uint16_t cid = client->cid;
417                 scan_parameters_service_emit_disconnected(packet_handler, cid);
418                 // finalize
419                 scan_parameters_service_finalize_client(client);
420             }
421             break;
422         default:
423             break;
424     }
425 }
426 
427 void scan_parameters_service_client_set(uint16_t scan_interval, uint16_t scan_window){
428     scan_parameters_service_scan_interval = scan_interval;
429     scan_parameters_service_scan_window = scan_window;
430 
431     btstack_linked_list_iterator_t it;
432     btstack_linked_list_iterator_init(&it, &clients);
433     while (btstack_linked_list_iterator_has_next(&it)){
434         scan_parameters_service_client_t * client = (scan_parameters_service_client_t*) btstack_linked_list_iterator_next(&it);
435         client->scan_interval_window_value_update = true;
436         scan_parameters_service_run_for_client(client);
437     }
438 }
439 
440 uint8_t scan_parameters_service_client_enable_notifications(uint16_t scan_parameters_service_cid){
441     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid);
442 
443     if (client == NULL){
444         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
445     }
446 
447     if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED) {
448         return ERROR_CODE_COMMAND_DISALLOWED;
449     }
450 
451     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
452     scan_parameters_service_run_for_client(client);
453     return ERROR_CODE_SUCCESS;
454 }
455 
456 uint8_t scan_parameters_service_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, uint16_t * scan_parameters_service_cid){
457     btstack_assert(packet_handler != NULL);
458 
459     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(con_handle);
460     if (client != NULL){
461         return ERROR_CODE_COMMAND_DISALLOWED;
462     }
463 
464     uint16_t cid = scan_parameters_service_get_next_cid();
465     if (scan_parameters_service_cid != NULL) {
466         *scan_parameters_service_cid = cid;
467     }
468 
469     client = scan_parameters_service_create_client(con_handle, cid);
470     if (client == NULL) {
471         return BTSTACK_MEMORY_ALLOC_FAILED;
472     }
473 
474     client->client_handler = packet_handler;
475     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
476     scan_parameters_service_run_for_client(client);
477     return ERROR_CODE_SUCCESS;
478 }
479 
480 uint8_t scan_parameters_service_client_disconnect(uint16_t scan_parameters_service_cid){
481     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid);
482     if (client == NULL){
483         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
484     }
485     // finalize connections
486     scan_parameters_service_finalize_client(client);
487     return ERROR_CODE_SUCCESS;
488 }
489 
490 void scan_parameters_service_client_init(void){
491     hci_event_callback_registration.callback = &handle_hci_event;
492     hci_add_event_handler(&hci_event_callback_registration);
493 }
494 
495 void scan_parameters_service_client_deinit(void){
496 }
497 
498