xref: /btstack/src/ble/gatt-service/scan_parameters_service_client.c (revision 503a627edab6ba8492c3d0cdd9ac598fe2b0f08a)
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__ "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/att_db.h"
53 #include "ble/core.h"
54 #include "ble/gatt_client.h"
55 #include "ble/sm.h"
56 #include "bluetooth_gatt.h"
57 #include "btstack_debug.h"
58 #include "btstack_event.h"
59 #include "btstack_run_loop.h"
60 #include "gap.h"
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 handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
140     UNUSED(packet_type);
141     UNUSED(channel);
142     UNUSED(size);
143 
144     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
145 
146     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
147     btstack_assert(client != NULL);
148     client->scan_interval_window_value_update = true;
149     scan_parameters_service_run_for_client(client);
150 }
151 
152 static void scan_parameters_service_run_for_client(scan_parameters_service_client_t * client){
153     uint8_t att_status;
154     gatt_client_service_t service;
155 
156     gatt_client_characteristic_t characteristic;
157 
158     switch (client->state){
159         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
160 #ifdef ENABLE_TESTING_SUPPORT
161             printf("\n\nQuery Services:\n");
162 #endif
163             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
164             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_SCAN_PARAMETERS);
165             // TODO handle status
166             UNUSED(att_status);
167             break;
168 
169         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
170 #ifdef ENABLE_TESTING_SUPPORT
171             printf("\n\nQuery Characteristics of service\n");
172 #endif
173             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
174             service.start_group_handle = client->start_handle;
175             service.end_group_handle = client->end_handle;
176             att_status = gatt_client_discover_characteristics_for_service(
177                 handle_gatt_client_event, client->con_handle, &service);
178             // TODO handle status
179             UNUSED(att_status);
180             break;
181 
182         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED:
183             if (client->scan_interval_window_value_update){
184                 client->scan_interval_window_value_update = false;
185 
186 #ifdef ENABLE_TESTING_SUPPORT
187                 printf("\n\nUpdate - interval %d, window %d:\n", scan_parameters_service_scan_interval, scan_parameters_service_scan_window);
188 #endif
189                 uint8_t value[4];
190                 little_endian_store_16(value, 0, scan_parameters_service_scan_interval);
191                 little_endian_store_16(value, 2, scan_parameters_service_scan_window);
192 
193                 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->scan_interval_window_value_handle, 4, value);
194                 // TODO handle status
195                 UNUSED(att_status);
196             }
197             break;
198 #ifdef ENABLE_TESTING_SUPPORT
199         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC:
200             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC;
201 
202             characteristic.value_handle = client->scan_refresh_value_handle;
203             characteristic.end_handle   =  client->scan_refresh_end_handle;
204 
205             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
206             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
207             UNUSED(att_status);
208             break;
209 #endif
210         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
211 #ifdef ENABLE_TESTING_SUPPORT
212             printf("    Notification configuration enable ");
213 #endif
214 
215             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
216 
217             characteristic.value_handle = client->scan_refresh_value_handle;
218             characteristic.end_handle = client->scan_refresh_end_handle;
219             characteristic.properties = client->scan_refresh_properties;
220 
221             // end of write marked in GATT_EVENT_QUERY_COMPLETE
222 
223             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
224 
225             if (att_status == ERROR_CODE_SUCCESS){
226                 gatt_client_listen_for_characteristic_value_updates(
227                             &client->notification_listener,
228                             &handle_notification_event, client->con_handle, &characteristic);
229             }
230             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
231             break;
232         default:
233             break;
234     }
235 }
236 
237 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
238     UNUSED(packet_type);
239     UNUSED(channel);
240     UNUSED(size);
241 
242     scan_parameters_service_client_t * client = NULL;
243     gatt_client_service_t service;
244     gatt_client_characteristic_t characteristic;
245     uint8_t att_status;
246 
247 #ifdef ENABLE_TESTING_SUPPORT
248     gatt_client_characteristic_descriptor_t characteristic_descriptor;
249 #endif
250 
251     switch(hci_event_packet_get_type(packet)){
252         case GATT_EVENT_SERVICE_QUERY_RESULT:
253             client = scan_parameters_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
254             btstack_assert(client != NULL);
255 
256             if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) {
257                 scan_parameters_service_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
258                 scan_parameters_service_finalize_client(client);
259                 break;
260             }
261 
262             gatt_event_service_query_result_get_service(packet, &service);
263             client->start_handle = service.start_group_handle;
264             client->end_handle = service.end_group_handle;
265 #ifdef ENABLE_TESTING_SUPPORT
266             printf("ScS Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle);
267 #endif
268             break;
269 
270         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
271             client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
272             btstack_assert(client != NULL);
273 
274             // found scan_interval_window_value_handle, check att_status
275             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
276             switch (characteristic.uuid16){
277                 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_INTERVAL_WINDOW:
278                     client->scan_interval_window_value_handle = characteristic.value_handle;
279 
280 #ifdef ENABLE_TESTING_SUPPORT
281                     printf("ScS Scan Interval Characteristic:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
282                         characteristic.start_handle,
283                         characteristic.properties,
284                         characteristic.value_handle, characteristic.uuid16);
285 #endif
286                     break;
287                 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_REFRESH:
288                     client->scan_refresh_value_handle = characteristic.value_handle;
289                     client->scan_refresh_end_handle = characteristic.end_handle;
290                     client->scan_refresh_properties = characteristic.properties;
291 
292 #ifdef ENABLE_TESTING_SUPPORT
293                     printf("ScS Scan Refresh 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                 default:
300                     break;
301             }
302             break;
303 
304 #ifdef ENABLE_TESTING_SUPPORT
305         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
306             client = scan_parameters_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
307             btstack_assert(client != NULL);
308 
309             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
310             if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
311                 printf("    Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
312                     characteristic_descriptor.handle,
313                     characteristic_descriptor.uuid16);
314             }
315             break;
316 
317         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
318             client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
319             btstack_assert(client != NULL);
320 
321             printf("    Received CCC value: ");
322             printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet),  gatt_event_characteristic_value_query_result_get_value_length(packet));
323             break;
324 #endif
325 
326         case GATT_EVENT_QUERY_COMPLETE:
327             client = scan_parameters_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
328             btstack_assert(client != NULL);
329 
330             att_status = gatt_event_query_complete_get_att_status(packet);
331 
332             switch (client->state){
333                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
334                     if (att_status != ATT_ERROR_SUCCESS){
335                         scan_parameters_service_emit_connection_established(client, att_status);
336                         scan_parameters_service_finalize_client(client);
337                         break;
338                     }
339 
340                     if (client->start_handle != 0){
341                         client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
342                         break;
343                     }
344 
345                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
346                     scan_parameters_service_finalize_client(client);
347                     break;
348 
349                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
350                     if (att_status != ATT_ERROR_SUCCESS){
351                         scan_parameters_service_emit_connection_established(client, att_status);
352                         scan_parameters_service_finalize_client(client);
353                         break;
354                     }
355                     if (client->scan_interval_window_value_handle == 0){
356                         scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
357                         scan_parameters_service_finalize_client(client);
358                         break;
359                     }
360 #ifdef ENABLE_TESTING_SUPPORT
361                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC;
362 #else
363                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
364                     client->scan_interval_window_value_update = true;
365                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
366 #endif
367                     break;
368 
369 #ifdef ENABLE_TESTING_SUPPORT
370                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC:
371                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
372                     client->scan_interval_window_value_update = true;
373                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
374                     break;
375 #endif
376                 default:
377                     break;
378             }
379             break;
380         default:
381             break;
382     }
383 
384     if (client != NULL){
385         scan_parameters_service_run_for_client(client);
386     }
387 }
388 
389 void scan_parameters_service_client_set(uint16_t scan_interval, uint16_t scan_window){
390     scan_parameters_service_scan_interval = scan_interval;
391     scan_parameters_service_scan_window = scan_window;
392 
393     btstack_linked_list_iterator_t it;
394     btstack_linked_list_iterator_init(&it, &clients);
395     while (btstack_linked_list_iterator_has_next(&it)){
396         scan_parameters_service_client_t * client = (scan_parameters_service_client_t*) btstack_linked_list_iterator_next(&it);
397         client->scan_interval_window_value_update = true;
398         scan_parameters_service_run_for_client(client);
399     }
400 }
401 
402 uint8_t scan_parameters_service_client_enable_notifications(uint16_t scan_parameters_service_cid){
403     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid);
404 
405     if (client == NULL){
406         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
407     }
408 
409     if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED) {
410         return ERROR_CODE_COMMAND_DISALLOWED;
411     }
412 
413     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
414     scan_parameters_service_run_for_client(client);
415     return ERROR_CODE_SUCCESS;
416 }
417 
418 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){
419     btstack_assert(packet_handler != NULL);
420 
421     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(con_handle);
422     if (client != NULL){
423         return ERROR_CODE_COMMAND_DISALLOWED;
424     }
425 
426     uint16_t cid = scan_parameters_service_get_next_cid();
427     if (scan_parameters_service_cid != NULL) {
428         *scan_parameters_service_cid = cid;
429     }
430 
431     client = scan_parameters_service_create_client(con_handle, cid);
432     if (client == NULL) {
433         return BTSTACK_MEMORY_ALLOC_FAILED;
434     }
435 
436     client->client_handler = packet_handler;
437     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
438     scan_parameters_service_run_for_client(client);
439     return ERROR_CODE_SUCCESS;
440 }
441 
442 uint8_t scan_parameters_service_client_disconnect(uint16_t scan_parameters_service_cid){
443     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid);
444     if (client == NULL){
445         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
446     }
447     // finalize connections
448     scan_parameters_service_finalize_client(client);
449     return ERROR_CODE_SUCCESS;
450 }
451 
452 void scan_parameters_service_client_init(void){}
453 
454 void scan_parameters_service_client_deinit(void){}
455 
456