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