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