xref: /btstack/src/ble/gatt-service/scan_parameters_service_client.c (revision 662759a70e84027dcec2f217834a0a5f160a5913)
178ae886bSMilanka Ringwald /*
278ae886bSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
378ae886bSMilanka Ringwald  *
478ae886bSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
578ae886bSMilanka Ringwald  * modification, are permitted provided that the following conditions
678ae886bSMilanka Ringwald  * are met:
778ae886bSMilanka Ringwald  *
878ae886bSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
978ae886bSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
1078ae886bSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1178ae886bSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
1278ae886bSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
1378ae886bSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
1478ae886bSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
1578ae886bSMilanka Ringwald  *    from this software without specific prior written permission.
1678ae886bSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
1778ae886bSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
1878ae886bSMilanka Ringwald  *    monetary gain.
1978ae886bSMilanka Ringwald  *
2078ae886bSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2178ae886bSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2278ae886bSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2578ae886bSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2678ae886bSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2778ae886bSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2878ae886bSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2978ae886bSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3078ae886bSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3178ae886bSMilanka Ringwald  * SUCH DAMAGE.
3278ae886bSMilanka Ringwald  *
3378ae886bSMilanka Ringwald  * Please inquire about commercial licensing options at
3478ae886bSMilanka Ringwald  * [email protected]
3578ae886bSMilanka Ringwald  *
3678ae886bSMilanka Ringwald  */
3778ae886bSMilanka Ringwald 
3878ae886bSMilanka Ringwald #define BTSTACK_FILE__ "scan_parameters_service_client.c"
3978ae886bSMilanka Ringwald 
4078ae886bSMilanka Ringwald #include "btstack_config.h"
4178ae886bSMilanka Ringwald 
4278ae886bSMilanka Ringwald #include <stdint.h>
4378ae886bSMilanka Ringwald #include <string.h>
4478ae886bSMilanka Ringwald 
45b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
46b3f0de51SMilanka Ringwald #include <stdio.h>
47b3f0de51SMilanka Ringwald #endif
4878ae886bSMilanka Ringwald 
4978ae886bSMilanka Ringwald #include "scan_parameters_service_client.h"
5078ae886bSMilanka Ringwald 
5178ae886bSMilanka Ringwald #include "btstack_memory.h"
5278ae886bSMilanka Ringwald #include "ble/core.h"
5378ae886bSMilanka Ringwald #include "ble/gatt_client.h"
5478ae886bSMilanka Ringwald #include "bluetooth_gatt.h"
5578ae886bSMilanka Ringwald #include "btstack_debug.h"
5678ae886bSMilanka Ringwald #include "btstack_event.h"
5778ae886bSMilanka Ringwald #include "btstack_run_loop.h"
5878ae886bSMilanka Ringwald #include "gap.h"
5978ae886bSMilanka Ringwald 
60fcf5cc99SMatthias Ringwald #include "ble/gatt_service_client.h"
61fcf5cc99SMatthias Ringwald 
627aa5195fSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
637aa5195fSMatthias Ringwald 
6478ae886bSMilanka Ringwald static btstack_linked_list_t clients;
6578ae886bSMilanka Ringwald static uint16_t scan_parameters_service_cid_counter = 0;
6678ae886bSMilanka Ringwald static uint16_t scan_parameters_service_scan_window = 0;
6778ae886bSMilanka Ringwald static uint16_t scan_parameters_service_scan_interval = 0;
6878ae886bSMilanka Ringwald 
6978ae886bSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
70fcf5cc99SMatthias Ringwald static void scan_parameters_service_send_next_query(void * context);
71fcf5cc99SMatthias Ringwald static btstack_context_callback_registration_t scan_parameters_service_handle_can_send_now;
7278ae886bSMilanka Ringwald 
scan_parameters_service_get_next_cid(void)7378ae886bSMilanka Ringwald static uint16_t scan_parameters_service_get_next_cid(void){
7478ae886bSMilanka Ringwald     if (scan_parameters_service_cid_counter == 0xffff) {
7578ae886bSMilanka Ringwald         scan_parameters_service_cid_counter = 1;
7678ae886bSMilanka Ringwald     } else {
7778ae886bSMilanka Ringwald         scan_parameters_service_cid_counter++;
7878ae886bSMilanka Ringwald     }
7978ae886bSMilanka Ringwald     return scan_parameters_service_cid_counter;
8078ae886bSMilanka Ringwald }
8178ae886bSMilanka Ringwald 
scan_parameters_client_request_send_gatt_query(scan_parameters_service_client_t * client)82fcf5cc99SMatthias Ringwald static uint8_t scan_parameters_client_request_send_gatt_query(scan_parameters_service_client_t * client){
83fcf5cc99SMatthias Ringwald     scan_parameters_service_handle_can_send_now.context = (void *) (uintptr_t)client->cid;
84fcf5cc99SMatthias Ringwald     uint8_t status = gatt_client_request_to_send_gatt_query(&scan_parameters_service_handle_can_send_now, client->con_handle);
85fcf5cc99SMatthias Ringwald     if (status != ERROR_CODE_SUCCESS){
86fcf5cc99SMatthias Ringwald         if (client->state >= SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE){
87fcf5cc99SMatthias Ringwald             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_IDLE;
88fcf5cc99SMatthias Ringwald         }
89fcf5cc99SMatthias Ringwald     }
90fcf5cc99SMatthias Ringwald     return status;
91fcf5cc99SMatthias Ringwald }
92fcf5cc99SMatthias Ringwald 
scan_parameters_service_create_client(hci_con_handle_t con_handle,uint16_t cid)9378ae886bSMilanka Ringwald static scan_parameters_service_client_t * scan_parameters_service_create_client(hci_con_handle_t con_handle, uint16_t cid){
9478ae886bSMilanka Ringwald     scan_parameters_service_client_t * client = btstack_memory_scan_parameters_service_client_get();
9578ae886bSMilanka Ringwald     if (!client){
9678ae886bSMilanka Ringwald         log_error("Not enough memory to create client");
9778ae886bSMilanka Ringwald         return NULL;
9878ae886bSMilanka Ringwald     }
9978ae886bSMilanka Ringwald 
10078ae886bSMilanka Ringwald     client->cid = cid;
10178ae886bSMilanka Ringwald     client->con_handle = con_handle;
10278ae886bSMilanka Ringwald     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_IDLE;
10378ae886bSMilanka Ringwald 
10478ae886bSMilanka Ringwald     client->start_handle = 0;
10578ae886bSMilanka Ringwald     client->end_handle = 0;
10678ae886bSMilanka Ringwald 
10778ae886bSMilanka Ringwald     client->scan_interval_window_value_handle = 0;
10878ae886bSMilanka Ringwald     client->scan_interval_window_value_update = false;
10978ae886bSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
11078ae886bSMilanka Ringwald     return client;
11178ae886bSMilanka Ringwald }
11278ae886bSMilanka Ringwald 
scan_parameters_service_finalize_client(scan_parameters_service_client_t * client)11378ae886bSMilanka Ringwald static void scan_parameters_service_finalize_client(scan_parameters_service_client_t * client){
114b3f0de51SMilanka Ringwald     gatt_client_stop_listening_for_characteristic_value_updates(&client->notification_listener);
11578ae886bSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
11678ae886bSMilanka Ringwald     btstack_memory_scan_parameters_service_client_free(client);
11778ae886bSMilanka Ringwald }
11878ae886bSMilanka Ringwald 
scan_parameters_service_get_client_for_con_handle(hci_con_handle_t con_handle)11978ae886bSMilanka Ringwald static scan_parameters_service_client_t * scan_parameters_service_get_client_for_con_handle(hci_con_handle_t con_handle){
12078ae886bSMilanka Ringwald     btstack_linked_list_iterator_t it;
12178ae886bSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
12278ae886bSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
12378ae886bSMilanka Ringwald         scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it);
12478ae886bSMilanka Ringwald         if (client->con_handle != con_handle) continue;
12578ae886bSMilanka Ringwald         return client;
12678ae886bSMilanka Ringwald     }
12778ae886bSMilanka Ringwald     return NULL;
12878ae886bSMilanka Ringwald }
12978ae886bSMilanka Ringwald 
scan_parameters_service_get_client_for_cid(uint16_t scan_parameters_service_cid)130b3f0de51SMilanka Ringwald static scan_parameters_service_client_t * scan_parameters_service_get_client_for_cid(uint16_t scan_parameters_service_cid){
13178ae886bSMilanka Ringwald     btstack_linked_list_iterator_t it;
13278ae886bSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
13378ae886bSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
13478ae886bSMilanka Ringwald         scan_parameters_service_client_t * client = (scan_parameters_service_client_t *)btstack_linked_list_iterator_next(&it);
135b3f0de51SMilanka Ringwald         if (client->cid != scan_parameters_service_cid) continue;
13678ae886bSMilanka Ringwald         return client;
13778ae886bSMilanka Ringwald     }
13878ae886bSMilanka Ringwald     return NULL;
13978ae886bSMilanka Ringwald }
14078ae886bSMilanka Ringwald 
scan_parameters_service_emit_connection_established(scan_parameters_service_client_t * client,uint8_t status)14178ae886bSMilanka Ringwald static void scan_parameters_service_emit_connection_established(scan_parameters_service_client_t * client, uint8_t status){
14278ae886bSMilanka Ringwald     uint8_t event[6];
14378ae886bSMilanka Ringwald     int pos = 0;
14478ae886bSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
14578ae886bSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
14678ae886bSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_CONNECTED;
14778ae886bSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
14878ae886bSMilanka Ringwald     pos += 2;
14978ae886bSMilanka Ringwald     event[pos++] = status;
150d4ac06ebSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, 0, event, sizeof(event));
15178ae886bSMilanka Ringwald }
15278ae886bSMilanka Ringwald 
scan_parameters_service_emit_disconnected(btstack_packet_handler_t packet_handler,uint16_t cid)1537aa5195fSMatthias Ringwald static void scan_parameters_service_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){
1547aa5195fSMatthias Ringwald     uint8_t event[5];
1557aa5195fSMatthias Ringwald     int pos = 0;
1567aa5195fSMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
1577aa5195fSMatthias Ringwald     event[pos++] = sizeof(event) - 2;
1587aa5195fSMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_DISCONNECTED;
1597aa5195fSMatthias Ringwald     little_endian_store_16(event, pos, cid);
1607aa5195fSMatthias Ringwald     pos += 2;
1617aa5195fSMatthias Ringwald     btstack_assert(pos == sizeof(event));
1627aa5195fSMatthias Ringwald     (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1637aa5195fSMatthias Ringwald }
1647aa5195fSMatthias Ringwald 
handle_notification_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)165b3f0de51SMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
166b3f0de51SMilanka Ringwald     UNUSED(packet_type);
167b3f0de51SMilanka Ringwald     UNUSED(channel);
168b3f0de51SMilanka Ringwald     UNUSED(size);
169b3f0de51SMilanka Ringwald 
170b3f0de51SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
171b3f0de51SMilanka Ringwald 
172b3f0de51SMilanka Ringwald     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
173b3f0de51SMilanka Ringwald     btstack_assert(client != NULL);
174b3f0de51SMilanka Ringwald     client->scan_interval_window_value_update = true;
175fcf5cc99SMatthias Ringwald     scan_parameters_client_request_send_gatt_query(client);
176b3f0de51SMilanka Ringwald }
17778ae886bSMilanka Ringwald 
scan_parameters_service_send_next_query(void * context)178fcf5cc99SMatthias Ringwald static void scan_parameters_service_send_next_query(void * context){
179fcf5cc99SMatthias Ringwald     uint16_t cid = (uint16_t)(uintptr_t)context;
180fcf5cc99SMatthias Ringwald     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(cid);
181fcf5cc99SMatthias Ringwald     if (client == NULL){
182fcf5cc99SMatthias Ringwald         return;
183fcf5cc99SMatthias Ringwald     }
184fcf5cc99SMatthias Ringwald 
18578ae886bSMilanka Ringwald     uint8_t att_status;
18678ae886bSMilanka Ringwald     gatt_client_service_t service;
18778ae886bSMilanka Ringwald 
188b3f0de51SMilanka Ringwald     gatt_client_characteristic_t characteristic;
189b3f0de51SMilanka Ringwald 
19078ae886bSMilanka Ringwald     switch (client->state){
19178ae886bSMilanka Ringwald         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
192b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
193b3f0de51SMilanka Ringwald             printf("\n\nQuery Services:\n");
194b3f0de51SMilanka Ringwald #endif
19578ae886bSMilanka Ringwald             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
19678ae886bSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_SCAN_PARAMETERS);
19778ae886bSMilanka Ringwald             // TODO handle status
19878ae886bSMilanka Ringwald             UNUSED(att_status);
19978ae886bSMilanka Ringwald             break;
20078ae886bSMilanka Ringwald 
20178ae886bSMilanka Ringwald         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
202b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
203b3f0de51SMilanka Ringwald             printf("\n\nQuery Characteristics of service\n");
204b3f0de51SMilanka Ringwald #endif
20578ae886bSMilanka Ringwald             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
20678ae886bSMilanka Ringwald             service.start_group_handle = client->start_handle;
20778ae886bSMilanka Ringwald             service.end_group_handle = client->end_handle;
208b3f0de51SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(
209b3f0de51SMilanka Ringwald                 handle_gatt_client_event, client->con_handle, &service);
21078ae886bSMilanka Ringwald             // TODO handle status
21178ae886bSMilanka Ringwald             UNUSED(att_status);
21278ae886bSMilanka Ringwald             break;
21378ae886bSMilanka Ringwald 
21478ae886bSMilanka Ringwald         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED:
21578ae886bSMilanka Ringwald             if (client->scan_interval_window_value_update){
216b3f0de51SMilanka Ringwald                 client->scan_interval_window_value_update = false;
217b3f0de51SMilanka Ringwald 
218b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
219b3f0de51SMilanka Ringwald                 printf("\n\nUpdate - interval %d, window %d:\n", scan_parameters_service_scan_interval, scan_parameters_service_scan_window);
220b3f0de51SMilanka Ringwald #endif
22178ae886bSMilanka Ringwald                 uint8_t value[4];
22278ae886bSMilanka Ringwald                 little_endian_store_16(value, 0, scan_parameters_service_scan_interval);
22378ae886bSMilanka Ringwald                 little_endian_store_16(value, 2, scan_parameters_service_scan_window);
22478ae886bSMilanka Ringwald 
22578ae886bSMilanka Ringwald                 att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->scan_interval_window_value_handle, 4, value);
22678ae886bSMilanka Ringwald                 // TODO handle status
22778ae886bSMilanka Ringwald                 UNUSED(att_status);
22878ae886bSMilanka Ringwald             }
22978ae886bSMilanka Ringwald             break;
230b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
231b3f0de51SMilanka Ringwald         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC:
232b3f0de51SMilanka Ringwald             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC;
233b3f0de51SMilanka Ringwald 
234b3f0de51SMilanka Ringwald             characteristic.value_handle = client->scan_refresh_value_handle;
235b3f0de51SMilanka Ringwald             characteristic.end_handle   =  client->scan_refresh_end_handle;
236b3f0de51SMilanka Ringwald 
237b3f0de51SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
238b3f0de51SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
239b3f0de51SMilanka Ringwald             UNUSED(att_status);
240b3f0de51SMilanka Ringwald             break;
241b3f0de51SMilanka Ringwald #endif
242b3f0de51SMilanka Ringwald         case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
243b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
244b3f0de51SMilanka Ringwald             printf("    Notification configuration enable ");
245b3f0de51SMilanka Ringwald #endif
246b3f0de51SMilanka Ringwald 
247b3f0de51SMilanka Ringwald             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
248b3f0de51SMilanka Ringwald 
249b3f0de51SMilanka Ringwald             characteristic.value_handle = client->scan_refresh_value_handle;
250b3f0de51SMilanka Ringwald             characteristic.end_handle = client->scan_refresh_end_handle;
251b3f0de51SMilanka Ringwald             characteristic.properties = client->scan_refresh_properties;
252b3f0de51SMilanka Ringwald 
253b3f0de51SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
254b3f0de51SMilanka Ringwald 
255b3f0de51SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
256b3f0de51SMilanka Ringwald 
257b3f0de51SMilanka Ringwald             if (att_status == ERROR_CODE_SUCCESS){
258b3f0de51SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
259b3f0de51SMilanka Ringwald                             &client->notification_listener,
260b3f0de51SMilanka Ringwald                             &handle_notification_event, client->con_handle, &characteristic);
261b3f0de51SMilanka Ringwald             }
262b3f0de51SMilanka Ringwald             client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
263b3f0de51SMilanka Ringwald             break;
26478ae886bSMilanka Ringwald         default:
26578ae886bSMilanka Ringwald             break;
26678ae886bSMilanka Ringwald     }
26778ae886bSMilanka Ringwald }
26878ae886bSMilanka Ringwald 
handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)26978ae886bSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
27078ae886bSMilanka Ringwald     UNUSED(packet_type);
27178ae886bSMilanka Ringwald     UNUSED(channel);
27278ae886bSMilanka Ringwald     UNUSED(size);
27378ae886bSMilanka Ringwald 
27478ae886bSMilanka Ringwald     scan_parameters_service_client_t * client = NULL;
27578ae886bSMilanka Ringwald     gatt_client_service_t service;
27678ae886bSMilanka Ringwald     gatt_client_characteristic_t characteristic;
277fcf5cc99SMatthias Ringwald     uint8_t status;
27878ae886bSMilanka Ringwald 
279b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
280b3f0de51SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
281b3f0de51SMilanka Ringwald #endif
282b3f0de51SMilanka Ringwald 
28378ae886bSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
28478ae886bSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
28578ae886bSMilanka Ringwald             client = scan_parameters_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
28678ae886bSMilanka Ringwald             btstack_assert(client != NULL);
28778ae886bSMilanka Ringwald 
28878ae886bSMilanka Ringwald             if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT) {
28978ae886bSMilanka Ringwald                 scan_parameters_service_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
29078ae886bSMilanka Ringwald                 scan_parameters_service_finalize_client(client);
2915fa2c39aSMilanka Ringwald                 return;
29278ae886bSMilanka Ringwald             }
29378ae886bSMilanka Ringwald 
29478ae886bSMilanka Ringwald             gatt_event_service_query_result_get_service(packet, &service);
29578ae886bSMilanka Ringwald             client->start_handle = service.start_group_handle;
29678ae886bSMilanka Ringwald             client->end_handle = service.end_group_handle;
297b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
298b3f0de51SMilanka Ringwald             printf("ScS Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle);
299b3f0de51SMilanka Ringwald #endif
30078ae886bSMilanka Ringwald             break;
30178ae886bSMilanka Ringwald 
30278ae886bSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
30378ae886bSMilanka Ringwald             client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
30478ae886bSMilanka Ringwald             btstack_assert(client != NULL);
30578ae886bSMilanka Ringwald 
30678ae886bSMilanka Ringwald             // found scan_interval_window_value_handle, check att_status
30778ae886bSMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
308b3f0de51SMilanka Ringwald             switch (characteristic.uuid16){
309b3f0de51SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_INTERVAL_WINDOW:
31078ae886bSMilanka Ringwald                     client->scan_interval_window_value_handle = characteristic.value_handle;
311b3f0de51SMilanka Ringwald 
312b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
313b3f0de51SMilanka Ringwald                     printf("ScS Scan Interval Characteristic:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
314b3f0de51SMilanka Ringwald                         characteristic.start_handle,
315b3f0de51SMilanka Ringwald                         characteristic.properties,
316b3f0de51SMilanka Ringwald                         characteristic.value_handle, characteristic.uuid16);
317b3f0de51SMilanka Ringwald #endif
31878ae886bSMilanka Ringwald                     break;
319b3f0de51SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_SCAN_REFRESH:
320b3f0de51SMilanka Ringwald                     client->scan_refresh_value_handle = characteristic.value_handle;
321b3f0de51SMilanka Ringwald                     client->scan_refresh_end_handle = characteristic.end_handle;
322b3f0de51SMilanka Ringwald                     client->scan_refresh_properties = characteristic.properties;
323b3f0de51SMilanka Ringwald 
324b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
325b3f0de51SMilanka Ringwald                     printf("ScS Scan Refresh Characteristic:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
326b3f0de51SMilanka Ringwald                         characteristic.start_handle,
327b3f0de51SMilanka Ringwald                         characteristic.properties,
328b3f0de51SMilanka Ringwald                         characteristic.value_handle, characteristic.uuid16);
329b3f0de51SMilanka Ringwald #endif
330b3f0de51SMilanka Ringwald                     break;
331b3f0de51SMilanka Ringwald                 default:
332b3f0de51SMilanka Ringwald                     break;
333b3f0de51SMilanka Ringwald             }
334b3f0de51SMilanka Ringwald             break;
335b3f0de51SMilanka Ringwald 
336b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
337b3f0de51SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
338b3f0de51SMilanka Ringwald             client = scan_parameters_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
339b3f0de51SMilanka Ringwald             btstack_assert(client != NULL);
340b3f0de51SMilanka Ringwald 
341b3f0de51SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
342b3f0de51SMilanka Ringwald             if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
343b3f0de51SMilanka Ringwald                 printf("    Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
344b3f0de51SMilanka Ringwald                     characteristic_descriptor.handle,
345b3f0de51SMilanka Ringwald                     characteristic_descriptor.uuid16);
346b3f0de51SMilanka Ringwald             }
347b3f0de51SMilanka Ringwald             break;
348b3f0de51SMilanka Ringwald 
349b3f0de51SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
350b3f0de51SMilanka Ringwald             client = scan_parameters_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
351b3f0de51SMilanka Ringwald             btstack_assert(client != NULL);
352b3f0de51SMilanka Ringwald 
353b3f0de51SMilanka Ringwald             printf("    Received CCC value: ");
354b3f0de51SMilanka Ringwald             printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet),  gatt_event_characteristic_value_query_result_get_value_length(packet));
355b3f0de51SMilanka Ringwald             break;
356b3f0de51SMilanka Ringwald #endif
35778ae886bSMilanka Ringwald 
35878ae886bSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
35978ae886bSMilanka Ringwald             client = scan_parameters_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
36078ae886bSMilanka Ringwald             btstack_assert(client != NULL);
36178ae886bSMilanka Ringwald 
36267e0500dSMatthias Ringwald             status = gatt_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet));
36378ae886bSMilanka Ringwald 
36478ae886bSMilanka Ringwald             switch (client->state){
36578ae886bSMilanka Ringwald                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
366fcf5cc99SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
367fcf5cc99SMatthias Ringwald                         scan_parameters_service_emit_connection_established(client, status);
36878ae886bSMilanka Ringwald                         scan_parameters_service_finalize_client(client);
3695fa2c39aSMilanka Ringwald                         return;
37078ae886bSMilanka Ringwald                     }
37178ae886bSMilanka Ringwald 
37278ae886bSMilanka Ringwald                     if (client->start_handle != 0){
373b3f0de51SMilanka Ringwald                         client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
37478ae886bSMilanka Ringwald                         break;
37578ae886bSMilanka Ringwald                     }
376b3f0de51SMilanka Ringwald 
377b3f0de51SMilanka Ringwald                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
378b3f0de51SMilanka Ringwald                     scan_parameters_service_finalize_client(client);
3795fa2c39aSMilanka Ringwald                     return;
38078ae886bSMilanka Ringwald 
38178ae886bSMilanka Ringwald                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
382fcf5cc99SMatthias Ringwald                     if (status != ERROR_CODE_SUCCESS){
383fcf5cc99SMatthias Ringwald                         scan_parameters_service_emit_connection_established(client, status);
38478ae886bSMilanka Ringwald                         scan_parameters_service_finalize_client(client);
38578ae886bSMilanka Ringwald                         break;
38678ae886bSMilanka Ringwald                     }
38778ae886bSMilanka Ringwald                     if (client->scan_interval_window_value_handle == 0){
38878ae886bSMilanka Ringwald                         scan_parameters_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
38978ae886bSMilanka Ringwald                         scan_parameters_service_finalize_client(client);
3905fa2c39aSMilanka Ringwald                         return;
39178ae886bSMilanka Ringwald                     }
392b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
393b3f0de51SMilanka Ringwald                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_CCC;
394b3f0de51SMilanka Ringwald #else
39578ae886bSMilanka Ringwald                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
39678ae886bSMilanka Ringwald                     client->scan_interval_window_value_update = true;
397b3f0de51SMilanka Ringwald                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
398b3f0de51SMilanka Ringwald #endif
39978ae886bSMilanka Ringwald                     break;
40078ae886bSMilanka Ringwald 
401b3f0de51SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
402b3f0de51SMilanka Ringwald                 case SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W4_CCC:
403b3f0de51SMilanka Ringwald                     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED;
404b3f0de51SMilanka Ringwald                     client->scan_interval_window_value_update = true;
405b3f0de51SMilanka Ringwald                     scan_parameters_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
406b3f0de51SMilanka Ringwald                     break;
407b3f0de51SMilanka Ringwald #endif
40878ae886bSMilanka Ringwald                 default:
40978ae886bSMilanka Ringwald                     break;
41078ae886bSMilanka Ringwald             }
411*662759a7SMilanka Ringwald             if (client != NULL){
412*662759a7SMilanka Ringwald                 scan_parameters_client_request_send_gatt_query(client);
413*662759a7SMilanka Ringwald             }
41478ae886bSMilanka Ringwald             break;
41578ae886bSMilanka Ringwald         default:
41678ae886bSMilanka Ringwald             break;
41778ae886bSMilanka Ringwald     }
41878ae886bSMilanka Ringwald 
419*662759a7SMilanka Ringwald 
42078ae886bSMilanka Ringwald }
42178ae886bSMilanka Ringwald 
handle_hci_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)4227aa5195fSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
4237aa5195fSMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
4247aa5195fSMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
4257aa5195fSMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
4267aa5195fSMatthias Ringwald 
4277aa5195fSMatthias Ringwald     hci_con_handle_t con_handle;
4287aa5195fSMatthias Ringwald     scan_parameters_service_client_t * client;
4297aa5195fSMatthias Ringwald 
4307aa5195fSMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
4317aa5195fSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
4327aa5195fSMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
4337aa5195fSMatthias Ringwald             client = scan_parameters_service_get_client_for_con_handle(con_handle);
4347aa5195fSMatthias Ringwald             if (client != NULL){
4357aa5195fSMatthias Ringwald                 // emit disconnected
4367aa5195fSMatthias Ringwald                 btstack_packet_handler_t packet_handler = client->client_handler;
4377aa5195fSMatthias Ringwald                 uint16_t cid = client->cid;
4387aa5195fSMatthias Ringwald                 scan_parameters_service_emit_disconnected(packet_handler, cid);
4397aa5195fSMatthias Ringwald                 // finalize
4407aa5195fSMatthias Ringwald                 scan_parameters_service_finalize_client(client);
4417aa5195fSMatthias Ringwald             }
4427aa5195fSMatthias Ringwald             break;
4437aa5195fSMatthias Ringwald         default:
4447aa5195fSMatthias Ringwald             break;
4457aa5195fSMatthias Ringwald     }
4467aa5195fSMatthias Ringwald }
4477aa5195fSMatthias Ringwald 
scan_parameters_service_client_set(uint16_t scan_interval,uint16_t scan_window)44878ae886bSMilanka Ringwald void scan_parameters_service_client_set(uint16_t scan_interval, uint16_t scan_window){
44978ae886bSMilanka Ringwald     scan_parameters_service_scan_interval = scan_interval;
45078ae886bSMilanka Ringwald     scan_parameters_service_scan_window = scan_window;
45178ae886bSMilanka Ringwald 
45278ae886bSMilanka Ringwald     btstack_linked_list_iterator_t it;
45378ae886bSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
45478ae886bSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
45578ae886bSMilanka Ringwald         scan_parameters_service_client_t * client = (scan_parameters_service_client_t*) btstack_linked_list_iterator_next(&it);
45678ae886bSMilanka Ringwald         client->scan_interval_window_value_update = true;
457fcf5cc99SMatthias Ringwald         scan_parameters_client_request_send_gatt_query(client);
45878ae886bSMilanka Ringwald     }
45978ae886bSMilanka Ringwald }
46078ae886bSMilanka Ringwald 
scan_parameters_service_client_enable_notifications(uint16_t scan_parameters_service_cid)461b3f0de51SMilanka Ringwald uint8_t scan_parameters_service_client_enable_notifications(uint16_t scan_parameters_service_cid){
462b3f0de51SMilanka Ringwald     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid);
463b3f0de51SMilanka Ringwald 
464b3f0de51SMilanka Ringwald     if (client == NULL){
465b3f0de51SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
466b3f0de51SMilanka Ringwald     }
467b3f0de51SMilanka Ringwald 
468b3f0de51SMilanka Ringwald     if (client->state != SCAN_PARAMETERS_SERVICE_CLIENT_STATE_CONNECTED) {
469b3f0de51SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
470b3f0de51SMilanka Ringwald     }
471b3f0de51SMilanka Ringwald 
472b3f0de51SMilanka Ringwald     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
473fcf5cc99SMatthias Ringwald     return scan_parameters_client_request_send_gatt_query(client);
474b3f0de51SMilanka Ringwald }
475b3f0de51SMilanka Ringwald 
scan_parameters_service_client_connect(hci_con_handle_t con_handle,btstack_packet_handler_t packet_handler,uint16_t * scan_parameters_service_cid)47678ae886bSMilanka Ringwald 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){
47778ae886bSMilanka Ringwald     btstack_assert(packet_handler != NULL);
47878ae886bSMilanka Ringwald 
47978ae886bSMilanka Ringwald     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_con_handle(con_handle);
48078ae886bSMilanka Ringwald     if (client != NULL){
48178ae886bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
48278ae886bSMilanka Ringwald     }
48378ae886bSMilanka Ringwald 
48478ae886bSMilanka Ringwald     uint16_t cid = scan_parameters_service_get_next_cid();
48578ae886bSMilanka Ringwald     if (scan_parameters_service_cid != NULL) {
48678ae886bSMilanka Ringwald         *scan_parameters_service_cid = cid;
48778ae886bSMilanka Ringwald     }
48878ae886bSMilanka Ringwald 
48978ae886bSMilanka Ringwald     client = scan_parameters_service_create_client(con_handle, cid);
49078ae886bSMilanka Ringwald     if (client == NULL) {
49178ae886bSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
49278ae886bSMilanka Ringwald     }
49378ae886bSMilanka Ringwald 
49478ae886bSMilanka Ringwald     client->client_handler = packet_handler;
49578ae886bSMilanka Ringwald     client->state = SCAN_PARAMETERS_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
496fcf5cc99SMatthias Ringwald     return scan_parameters_client_request_send_gatt_query(client);
49778ae886bSMilanka Ringwald }
49878ae886bSMilanka Ringwald 
scan_parameters_service_client_disconnect(uint16_t scan_parameters_service_cid)49978ae886bSMilanka Ringwald uint8_t scan_parameters_service_client_disconnect(uint16_t scan_parameters_service_cid){
50078ae886bSMilanka Ringwald     scan_parameters_service_client_t * client = scan_parameters_service_get_client_for_cid(scan_parameters_service_cid);
50178ae886bSMilanka Ringwald     if (client == NULL){
50278ae886bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
50378ae886bSMilanka Ringwald     }
50478ae886bSMilanka Ringwald     // finalize connections
50578ae886bSMilanka Ringwald     scan_parameters_service_finalize_client(client);
50678ae886bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
50778ae886bSMilanka Ringwald }
50878ae886bSMilanka Ringwald 
scan_parameters_service_client_init(void)5097aa5195fSMatthias Ringwald void scan_parameters_service_client_init(void){
5107aa5195fSMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
5117aa5195fSMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
512fcf5cc99SMatthias Ringwald     scan_parameters_service_handle_can_send_now.callback = &scan_parameters_service_send_next_query;
5137aa5195fSMatthias Ringwald }
51478ae886bSMilanka Ringwald 
scan_parameters_service_client_deinit(void)5157aa5195fSMatthias Ringwald void scan_parameters_service_client_deinit(void){
5167aa5195fSMatthias Ringwald }
51778ae886bSMilanka Ringwald 
518