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