xref: /btstack/src/ble/gatt_service_client.c (revision 13a624a6d940b0c052375dd10b7ae103edec0b14)
112879e75SMatthias Ringwald /*
212879e75SMatthias Ringwald  * Copyright (C) 2023 BlueKitchen GmbH
312879e75SMatthias Ringwald  *
412879e75SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
512879e75SMatthias Ringwald  * modification, are permitted provided that the following conditions
612879e75SMatthias Ringwald  * are met:
712879e75SMatthias Ringwald  *
812879e75SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
912879e75SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
1012879e75SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1112879e75SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
1212879e75SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
1312879e75SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
1412879e75SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
1512879e75SMatthias Ringwald  *    from this software without specific prior written permission.
1612879e75SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
1712879e75SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
1812879e75SMatthias Ringwald  *    monetary gain.
1912879e75SMatthias Ringwald  *
2012879e75SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2112879e75SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2212879e75SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2312879e75SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
2412879e75SMatthias Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2512879e75SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2612879e75SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2712879e75SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2812879e75SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2912879e75SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3012879e75SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3112879e75SMatthias Ringwald  * SUCH DAMAGE.
3212879e75SMatthias Ringwald  *
3312879e75SMatthias Ringwald  * Please inquire about commercial licensing options at
3412879e75SMatthias Ringwald  * [email protected]
3512879e75SMatthias Ringwald  *
3612879e75SMatthias Ringwald  */
3712879e75SMatthias Ringwald 
3812879e75SMatthias Ringwald #define BTSTACK_FILE__ "gatt_service_client_helper.c"
3912879e75SMatthias Ringwald 
4012879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4112879e75SMatthias Ringwald #include <stdio.h>
4212879e75SMatthias Ringwald #endif
4312879e75SMatthias Ringwald 
4412879e75SMatthias Ringwald #include <stdint.h>
4512879e75SMatthias Ringwald #include <string.h>
4612879e75SMatthias Ringwald 
4712879e75SMatthias Ringwald #include "ble/gatt_service_client.h"
4812879e75SMatthias Ringwald 
4912879e75SMatthias Ringwald #include "bluetooth_gatt.h"
5012879e75SMatthias Ringwald #include "btstack_debug.h"
5112879e75SMatthias Ringwald #include "btstack_event.h"
5212879e75SMatthias Ringwald 
5312879e75SMatthias Ringwald static void gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
5412879e75SMatthias Ringwald 
5512879e75SMatthias Ringwald static bool gatt_service_client_intitialized = false;
5612879e75SMatthias Ringwald static uint16_t gatt_service_client_service_cid;
5712879e75SMatthias Ringwald static btstack_linked_list_t gatt_service_clients;
5812879e75SMatthias Ringwald static btstack_packet_callback_registration_t gatt_service_client_hci_callback_registration;
5912879e75SMatthias Ringwald 
6012879e75SMatthias Ringwald // LE Audio Service Client helper functions
6112879e75SMatthias Ringwald static void gatt_service_client_finalize_connection(gatt_service_client_t * client, gatt_service_client_connection_t * connection){
6212879e75SMatthias Ringwald     btstack_assert(client != NULL);
6312879e75SMatthias Ringwald     btstack_linked_list_remove(&client->connections, (btstack_linked_item_t*) connection);
6412879e75SMatthias Ringwald }
6512879e75SMatthias Ringwald 
6612879e75SMatthias Ringwald static gatt_service_client_t * gatt_service_client_get_service_client_for_id(uint16_t service_cid){
6712879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
6812879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it,  &gatt_service_clients);
6912879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
7012879e75SMatthias Ringwald         gatt_service_client_t * service_client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&it);
7112879e75SMatthias Ringwald         if (service_client->service_id == service_cid) {
7212879e75SMatthias Ringwald             return service_client;
7312879e75SMatthias Ringwald         };
7412879e75SMatthias Ringwald     }
7512879e75SMatthias Ringwald     return NULL;
7612879e75SMatthias Ringwald }
7712879e75SMatthias Ringwald 
7812879e75SMatthias Ringwald static gatt_service_client_connection_t * gatt_service_client_get_connection_for_con_handle_and_service_index(const gatt_service_client_t * client, hci_con_handle_t con_handle, uint8_t service_index){
7912879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
8012879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
8112879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
8212879e75SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
8312879e75SMatthias Ringwald         if (connection->con_handle != con_handle) continue;
8412879e75SMatthias Ringwald         if (connection->service_index != service_index) continue;
8512879e75SMatthias Ringwald         return connection;
8612879e75SMatthias Ringwald     }
8712879e75SMatthias Ringwald     return NULL;
8812879e75SMatthias Ringwald }
8912879e75SMatthias Ringwald 
9012879e75SMatthias Ringwald static gatt_service_client_connection_t * gatt_service_client_get_connection_for_cid(
9112879e75SMatthias Ringwald         const gatt_service_client_t *client, uint16_t connection_cid){
9212879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
9312879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
9412879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
9512879e75SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
9612879e75SMatthias Ringwald         if (connection->cid != connection_cid) continue;
9712879e75SMatthias Ringwald         return connection;
9812879e75SMatthias Ringwald     }
9912879e75SMatthias Ringwald     return NULL;
10012879e75SMatthias Ringwald }
10112879e75SMatthias Ringwald 
10212879e75SMatthias Ringwald uint16_t gatt_service_client_get_mtu(const gatt_service_client_connection_t *connection) {
10312879e75SMatthias Ringwald     uint16_t mtu = 0;
10412879e75SMatthias Ringwald     gatt_client_get_mtu(connection->con_handle, &mtu);
10512879e75SMatthias Ringwald     return mtu;
10612879e75SMatthias Ringwald }
10712879e75SMatthias Ringwald 
10812879e75SMatthias Ringwald uint16_t gatt_service_client_get_connection_id(const gatt_service_client_connection_t * connection){
10912879e75SMatthias Ringwald     return connection->cid;
11012879e75SMatthias Ringwald 
11112879e75SMatthias Ringwald }
11212879e75SMatthias Ringwald 
11312879e75SMatthias Ringwald hci_con_handle_t gatt_service_client_get_con_handle(const gatt_service_client_connection_t * connection){
11412879e75SMatthias Ringwald     return connection->con_handle;
11512879e75SMatthias Ringwald }
11612879e75SMatthias Ringwald 
11712879e75SMatthias Ringwald uint8_t gatt_service_client_get_service_index(const gatt_service_client_connection_t * connection){
11812879e75SMatthias Ringwald     return connection->service_index;
11912879e75SMatthias Ringwald }
12012879e75SMatthias Ringwald 
12112879e75SMatthias Ringwald uint16_t gatt_service_client_characteristic_uuid16_for_index(const gatt_service_client_t * client, uint8_t characteristic_index){
12212879e75SMatthias Ringwald     if (characteristic_index < client->characteristics_desc16_num){
12312879e75SMatthias Ringwald         return client->characteristics_desc16[characteristic_index];
12412879e75SMatthias Ringwald     } else {
12512879e75SMatthias Ringwald         return 0;
12612879e75SMatthias Ringwald     }
12712879e75SMatthias Ringwald }
12812879e75SMatthias Ringwald 
12912879e75SMatthias Ringwald uint16_t gatt_service_client_characteristic_value_handle_for_index(const gatt_service_client_connection_t *connection,
13012879e75SMatthias Ringwald                                                                    uint8_t characteristic_index) {
13112879e75SMatthias Ringwald     return connection->characteristics[characteristic_index].value_handle;
13212879e75SMatthias Ringwald }
13312879e75SMatthias Ringwald 
13412879e75SMatthias Ringwald uint8_t gatt_service_client_characteristic_index_for_value_handle(const gatt_service_client_connection_t *connection,
13512879e75SMatthias Ringwald                                                                   uint16_t value_handle) {
13612879e75SMatthias Ringwald     for (int i = 0; i < connection->client->characteristics_desc16_num; i++){
13712879e75SMatthias Ringwald         if (connection->characteristics[i].value_handle == value_handle) {
13812879e75SMatthias Ringwald             return i;
13912879e75SMatthias Ringwald         }
14012879e75SMatthias Ringwald     }
14112879e75SMatthias Ringwald     return GATT_SERVICE_CLIENT_INVALID_INDEX;
14212879e75SMatthias Ringwald }
14312879e75SMatthias Ringwald 
14412879e75SMatthias Ringwald uint8_t gatt_service_client_att_status_to_error_code(uint8_t att_error_code){
145*13a624a6SMatthias Ringwald     return gatt_client_att_status_to_error_code(att_error_code);
14612879e75SMatthias Ringwald }
14712879e75SMatthias Ringwald 
14812879e75SMatthias Ringwald static void gatt_service_client_emit_connected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid, uint8_t status){
14912879e75SMatthias Ringwald     btstack_assert(event_callback != NULL);
15012879e75SMatthias Ringwald 
15112879e75SMatthias Ringwald     log_info("GATT Client Helper, connected 0x%04x, status 0x%02x", con_handle, status);
15212879e75SMatthias Ringwald 
15312879e75SMatthias Ringwald     uint8_t event[9];
15412879e75SMatthias Ringwald     int pos = 0;
15512879e75SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
15612879e75SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
15712879e75SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_CONNECTED;
15812879e75SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
15912879e75SMatthias Ringwald     pos += 2;
16012879e75SMatthias Ringwald     little_endian_store_16(event, pos, cid);
16112879e75SMatthias Ringwald     pos += 2;
16212879e75SMatthias Ringwald     event[pos++] = 0; // num included services
16312879e75SMatthias Ringwald     event[pos++] = status;
16412879e75SMatthias Ringwald     (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
16512879e75SMatthias Ringwald }
16612879e75SMatthias Ringwald 
16712879e75SMatthias Ringwald static void gatt_service_client_emit_disconnected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid){
16812879e75SMatthias Ringwald     btstack_assert(event_callback != NULL);
16912879e75SMatthias Ringwald 
17012879e75SMatthias Ringwald     uint8_t event[7];
17112879e75SMatthias Ringwald     int pos = 0;
17212879e75SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
17312879e75SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
17412879e75SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED;
17512879e75SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
17612879e75SMatthias Ringwald     pos += 2;
17712879e75SMatthias Ringwald     little_endian_store_16(event, pos, cid);
17812879e75SMatthias Ringwald     pos += 2;
17912879e75SMatthias Ringwald     (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
18012879e75SMatthias Ringwald }
18112879e75SMatthias Ringwald 
18212879e75SMatthias Ringwald static uint16_t gatt_service_client_get_next_cid(gatt_service_client_t * client){
18312879e75SMatthias Ringwald     client->cid_counter = btstack_next_cid_ignoring_zero(client->cid_counter);
18412879e75SMatthias Ringwald     return client->cid_counter;
18512879e75SMatthias Ringwald }
18612879e75SMatthias Ringwald 
18712879e75SMatthias Ringwald static bool gatt_service_client_more_descriptor_queries(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) {
18812879e75SMatthias Ringwald     bool next_query_found = false;
18912879e75SMatthias Ringwald     while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) {
19012879e75SMatthias Ringwald         uint16_t notify_or_indicate = ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE;
19112879e75SMatthias Ringwald         if ((connection->characteristics[connection->characteristic_index].properties & notify_or_indicate) != 0u){
19212879e75SMatthias Ringwald             next_query_found = true;
19312879e75SMatthias Ringwald             break;
19412879e75SMatthias Ringwald         }
19512879e75SMatthias Ringwald         connection->characteristic_index++;
19612879e75SMatthias Ringwald     }
19712879e75SMatthias Ringwald     return next_query_found;
19812879e75SMatthias Ringwald }
19912879e75SMatthias Ringwald 
20012879e75SMatthias Ringwald static bool gatt_service_client_have_more_notifications_to_enable(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) {
20112879e75SMatthias Ringwald     bool next_query_found = false;
20212879e75SMatthias Ringwald     while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) {
20312879e75SMatthias Ringwald         if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0) {
20412879e75SMatthias Ringwald             next_query_found = true;
20512879e75SMatthias Ringwald             break;
20612879e75SMatthias Ringwald         }
20712879e75SMatthias Ringwald         connection->characteristic_index++;
20812879e75SMatthias Ringwald     }
20912879e75SMatthias Ringwald     return next_query_found;
21012879e75SMatthias Ringwald }
21112879e75SMatthias Ringwald 
21212879e75SMatthias Ringwald static uint8_t gatt_service_client_register_notification(gatt_service_client_t * client,
21312879e75SMatthias Ringwald                                                          gatt_service_client_connection_t *connection) {
21412879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
21512879e75SMatthias Ringwald     uint8_t status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
21612879e75SMatthias Ringwald 
21712879e75SMatthias Ringwald     if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0){
21812879e75SMatthias Ringwald         characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle;
21912879e75SMatthias Ringwald         characteristic.end_handle = connection->characteristics[connection->characteristic_index].end_handle;
22012879e75SMatthias Ringwald         characteristic.properties = connection->characteristics[connection->characteristic_index].properties;
22112879e75SMatthias Ringwald 
22212879e75SMatthias Ringwald         if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u){
22312879e75SMatthias Ringwald             status = gatt_client_write_client_characteristic_configuration_with_context(
22412879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
22512879e75SMatthias Ringwald                     connection->con_handle,
22612879e75SMatthias Ringwald                     &characteristic,
22712879e75SMatthias Ringwald                     GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION,
22812879e75SMatthias Ringwald                     client->service_id,
22912879e75SMatthias Ringwald                     connection->cid);
23012879e75SMatthias Ringwald         } else if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){
23112879e75SMatthias Ringwald             status = gatt_client_write_client_characteristic_configuration_with_context(
23212879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
23312879e75SMatthias Ringwald                     connection->con_handle,
23412879e75SMatthias Ringwald                     &characteristic,
23512879e75SMatthias Ringwald                     GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION,
23612879e75SMatthias Ringwald                     client->service_id,
23712879e75SMatthias Ringwald                     connection->cid);
23812879e75SMatthias Ringwald         }
23912879e75SMatthias Ringwald     }
24012879e75SMatthias Ringwald     return status;
24112879e75SMatthias Ringwald }
24212879e75SMatthias Ringwald 
24312879e75SMatthias Ringwald 
24412879e75SMatthias Ringwald static void gatt_service_client_run_for_client(gatt_service_client_t * client, gatt_service_client_connection_t * connection){
24512879e75SMatthias Ringwald     uint8_t status = ATT_ERROR_SUCCESS;
24612879e75SMatthias Ringwald     gatt_client_service_t service;
24712879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
24812879e75SMatthias Ringwald 
24912879e75SMatthias Ringwald     switch (connection->state){
25012879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE:
25112879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
25212879e75SMatthias Ringwald             status = gatt_client_discover_primary_services_by_uuid16_with_context(
25312879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
25412879e75SMatthias Ringwald                     connection->con_handle,
25512879e75SMatthias Ringwald                     connection->service_uuid16,
25612879e75SMatthias Ringwald                     client->service_id,
25712879e75SMatthias Ringwald                     connection->cid);
25812879e75SMatthias Ringwald             break;
25912879e75SMatthias Ringwald 
26012879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
26112879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
26212879e75SMatthias Ringwald             printf("Read characteristics [service 0x%04x]:\n", connection->service_uuid16);
26312879e75SMatthias Ringwald #endif
26412879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
26512879e75SMatthias Ringwald 
26612879e75SMatthias Ringwald             service.start_group_handle = connection->start_handle;
26712879e75SMatthias Ringwald             service.end_group_handle = connection->end_handle;
26812879e75SMatthias Ringwald             service.uuid16 = connection->service_uuid16;
26912879e75SMatthias Ringwald 
27012879e75SMatthias Ringwald             status = gatt_client_discover_characteristics_for_service_with_context(
27112879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
27212879e75SMatthias Ringwald                     connection->con_handle,
27312879e75SMatthias Ringwald                     &service,
27412879e75SMatthias Ringwald                     client->service_id,
27512879e75SMatthias Ringwald                     connection->cid);
27612879e75SMatthias Ringwald             break;
27712879e75SMatthias Ringwald 
27812879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS:
27912879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
28012879e75SMatthias Ringwald             printf("Read client characteristic descriptors for characteristic[%u, uuid16 0x%04x, value_handle 0x%04x]:\n",
28112879e75SMatthias Ringwald                 connection->characteristic_index,
28212879e75SMatthias Ringwald                 client->characteristics_desc16[connection->characteristic_index],
28312879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].value_handle);
28412879e75SMatthias Ringwald #endif
28512879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT;
28612879e75SMatthias Ringwald             characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle;
28712879e75SMatthias Ringwald             characteristic.properties   = connection->characteristics[connection->characteristic_index].properties;
28812879e75SMatthias Ringwald             characteristic.end_handle   = connection->characteristics[connection->characteristic_index].end_handle;
28912879e75SMatthias Ringwald 
29012879e75SMatthias Ringwald             (void) gatt_client_discover_characteristic_descriptors_with_context(
29112879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
29212879e75SMatthias Ringwald                     connection->con_handle,
29312879e75SMatthias Ringwald                     &characteristic,
29412879e75SMatthias Ringwald                     client->service_id,
29512879e75SMatthias Ringwald                     connection->cid);
29612879e75SMatthias Ringwald             break;
29712879e75SMatthias Ringwald 
29812879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION:
29912879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
30012879e75SMatthias Ringwald             if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){
30112879e75SMatthias Ringwald                 printf("Register notification for characteristic");
30212879e75SMatthias Ringwald             } else {
30312879e75SMatthias Ringwald                 printf("Register indication for characteristic");
30412879e75SMatthias Ringwald             }
30512879e75SMatthias Ringwald 
30612879e75SMatthias Ringwald             printf("[%u, uuid16 0x%04x, ccd handle 0x%04x]\n",
30712879e75SMatthias Ringwald                 connection->characteristic_index,
30812879e75SMatthias Ringwald                 client->characteristics_desc16[connection->characteristic_index],
30912879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle);
31012879e75SMatthias Ringwald #endif
31112879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED;
31212879e75SMatthias Ringwald             status = gatt_service_client_register_notification(client, connection);
31312879e75SMatthias Ringwald             connection->characteristic_index++;
31412879e75SMatthias Ringwald 
31512879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
31612879e75SMatthias Ringwald             if (status != ERROR_CODE_SUCCESS) {
31712879e75SMatthias Ringwald                 printf("Notification not supported, status 0%02X\n.", status);
31812879e75SMatthias Ringwald             }
31912879e75SMatthias Ringwald #else
32012879e75SMatthias Ringwald             UNUSED(status);
32112879e75SMatthias Ringwald #endif
32212879e75SMatthias Ringwald             return;
32312879e75SMatthias Ringwald 
32412879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_CONNECTED:
32512879e75SMatthias Ringwald             // TODO
32612879e75SMatthias Ringwald             break;
32712879e75SMatthias Ringwald         default:
32812879e75SMatthias Ringwald             break;
32912879e75SMatthias Ringwald     }
33012879e75SMatthias Ringwald 
33112879e75SMatthias Ringwald     if (status != ATT_ERROR_SUCCESS){
332*13a624a6SMatthias Ringwald         gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_client_att_status_to_error_code(status));
33312879e75SMatthias Ringwald         gatt_service_client_finalize_connection(client, connection);
33412879e75SMatthias Ringwald     }
33512879e75SMatthias Ringwald }
33612879e75SMatthias Ringwald 
33712879e75SMatthias Ringwald // @return true if client valid / run function should be called
33812879e75SMatthias Ringwald static bool gatt_service_client_handle_query_complete(gatt_service_client_t *client,
33912879e75SMatthias Ringwald                                                       gatt_service_client_connection_t *connection,
34012879e75SMatthias Ringwald                                                       uint8_t status) {
34112879e75SMatthias Ringwald     btstack_assert(client != NULL);
34212879e75SMatthias Ringwald     btstack_assert(connection != NULL);
34312879e75SMatthias Ringwald 
34412879e75SMatthias Ringwald     if (status != ATT_ERROR_SUCCESS){
34512879e75SMatthias Ringwald         switch (connection->state){
34612879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
34712879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
34812879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
34912879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
350*13a624a6SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_client_att_status_to_error_code(status));
35112879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
35212879e75SMatthias Ringwald                 return false;
35312879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_CONNECTED:
35412879e75SMatthias Ringwald                 break;
35512879e75SMatthias Ringwald             default:
35612879e75SMatthias Ringwald                 btstack_unreachable();
35712879e75SMatthias Ringwald                 break;
35812879e75SMatthias Ringwald         }
35912879e75SMatthias Ringwald     }
36012879e75SMatthias Ringwald 
36112879e75SMatthias Ringwald     switch (connection->state){
36212879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
36312879e75SMatthias Ringwald             if (connection->service_instances_num == 0){
36412879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
36512879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
36612879e75SMatthias Ringwald                 return false;
36712879e75SMatthias Ringwald             }
36812879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
36912879e75SMatthias Ringwald             connection->characteristic_index = 0;
37012879e75SMatthias Ringwald             break;
37112879e75SMatthias Ringwald 
37212879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
37312879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
37412879e75SMatthias Ringwald             connection->characteristic_index = 0;
37512879e75SMatthias Ringwald             break;
37612879e75SMatthias Ringwald 
37712879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
37812879e75SMatthias Ringwald             if (gatt_service_client_more_descriptor_queries(client, connection)){
37912879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
38012879e75SMatthias Ringwald                 break;
38112879e75SMatthias Ringwald             }
38212879e75SMatthias Ringwald 
38312879e75SMatthias Ringwald             connection->characteristic_index = 0;
38412879e75SMatthias Ringwald             if (gatt_service_client_have_more_notifications_to_enable(client, connection)){
38512879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
38612879e75SMatthias Ringwald             } else {
38712879e75SMatthias Ringwald                 connection->characteristic_index = 0;
38812879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED;
38912879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS);
39012879e75SMatthias Ringwald             }
39112879e75SMatthias Ringwald             break;
39212879e75SMatthias Ringwald 
39312879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
39412879e75SMatthias Ringwald             if (gatt_service_client_have_more_notifications_to_enable(client, connection)){
39512879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
39612879e75SMatthias Ringwald             } else {
39712879e75SMatthias Ringwald                 // all notifications registered, start listening
39812879e75SMatthias Ringwald                 gatt_client_service_t service;
39912879e75SMatthias Ringwald                 service.start_group_handle = connection->start_handle;
40012879e75SMatthias Ringwald                 service.end_group_handle = connection->end_handle;
40112879e75SMatthias Ringwald 
40212879e75SMatthias Ringwald                 gatt_client_listen_for_service_characteristic_value_updates(&connection->notification_listener, client->packet_handler,
40312879e75SMatthias Ringwald                                                                             connection->con_handle, &service, client->service_id, connection->cid);
40412879e75SMatthias Ringwald 
40512879e75SMatthias Ringwald                 connection->characteristic_index = 0;
40612879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED;
40712879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS);
40812879e75SMatthias Ringwald             }
40912879e75SMatthias Ringwald 
41012879e75SMatthias Ringwald             break;
41112879e75SMatthias Ringwald 
41212879e75SMatthias Ringwald         default:
41312879e75SMatthias Ringwald             break;
41412879e75SMatthias Ringwald 
41512879e75SMatthias Ringwald     }
41612879e75SMatthias Ringwald     // TODO run_for_client
41712879e75SMatthias Ringwald     return true;
41812879e75SMatthias Ringwald }
41912879e75SMatthias Ringwald 
42012879e75SMatthias Ringwald static uint8_t gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(
42112879e75SMatthias Ringwald         gatt_service_client_t * client,
42212879e75SMatthias Ringwald         gatt_service_client_connection_t * connection,
42312879e75SMatthias Ringwald         uint16_t uuid16){
42412879e75SMatthias Ringwald 
42512879e75SMatthias Ringwald         uint8_t index = 0xff;
42612879e75SMatthias Ringwald 
42712879e75SMatthias Ringwald         uint8_t i;
42812879e75SMatthias Ringwald 
42912879e75SMatthias Ringwald         for (i = 0; i < client->characteristics_desc16_num; i++){
43012879e75SMatthias Ringwald             if (client->characteristics_desc16[i] == uuid16){
43112879e75SMatthias Ringwald                 // allow for more then one instance of the same characteristic (as in OTS client)
43212879e75SMatthias Ringwald                 if (connection->characteristics[i].value_handle != 0){
43312879e75SMatthias Ringwald                    continue;
43412879e75SMatthias Ringwald                 }
43512879e75SMatthias Ringwald                 index = i;
43612879e75SMatthias Ringwald                 break;
43712879e75SMatthias Ringwald             }
43812879e75SMatthias Ringwald         }
43912879e75SMatthias Ringwald         return index;
44012879e75SMatthias Ringwald }
44112879e75SMatthias Ringwald 
44212879e75SMatthias Ringwald static void gatt_service_client_handle_disconnect(hci_con_handle_t con_handle){
44312879e75SMatthias Ringwald     btstack_linked_list_iterator_t service_it;
44412879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&service_it, &gatt_service_clients);
44512879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&service_it)){
44612879e75SMatthias Ringwald         gatt_service_client_t * client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&service_it);
44712879e75SMatthias Ringwald         btstack_linked_list_iterator_t connection_it;
44812879e75SMatthias Ringwald         btstack_linked_list_iterator_init(&connection_it, &client->connections);
44912879e75SMatthias Ringwald         while (btstack_linked_list_iterator_has_next(&connection_it)){
45012879e75SMatthias Ringwald             gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&connection_it);
45112879e75SMatthias Ringwald             if (connection->con_handle == con_handle) {
45212879e75SMatthias Ringwald                 gatt_service_client_emit_disconnected(client->packet_handler, connection->con_handle, connection->cid);
45312879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
45412879e75SMatthias Ringwald             }
45512879e75SMatthias Ringwald         }
45612879e75SMatthias Ringwald     }
45712879e75SMatthias Ringwald }
45812879e75SMatthias Ringwald 
45912879e75SMatthias Ringwald static void
46012879e75SMatthias Ringwald gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
46112879e75SMatthias Ringwald     UNUSED(channel);
46212879e75SMatthias Ringwald     UNUSED(size);
46312879e75SMatthias Ringwald 
46412879e75SMatthias Ringwald     if (packet_type != HCI_EVENT_PACKET) return;
46512879e75SMatthias Ringwald 
46612879e75SMatthias Ringwald     gatt_service_client_t * client;
46712879e75SMatthias Ringwald     gatt_service_client_connection_t * connection;
46812879e75SMatthias Ringwald     gatt_client_service_t service;
46912879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
47012879e75SMatthias Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
47112879e75SMatthias Ringwald     uint8_t characteristic_index;
47212879e75SMatthias Ringwald 
47312879e75SMatthias Ringwald     bool call_run = true;
47412879e75SMatthias Ringwald     switch (hci_event_packet_get_type(packet)){
47512879e75SMatthias Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
47612879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_service_query_result_get_service_id(packet));
47712879e75SMatthias Ringwald             btstack_assert(client != NULL);
47812879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_service_query_result_get_connection_id(packet));
47912879e75SMatthias Ringwald             btstack_assert(connection != NULL);
48012879e75SMatthias Ringwald 
48112879e75SMatthias Ringwald             if (connection->service_instances_num < 1){
48212879e75SMatthias Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
48312879e75SMatthias Ringwald                 connection->start_handle = service.start_group_handle;
48412879e75SMatthias Ringwald                 connection->end_handle   = service.end_group_handle;
48512879e75SMatthias Ringwald 
48612879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
48712879e75SMatthias Ringwald                 printf("Service: start handle 0x%04X, end handle 0x%04X\n", connection->start_handle, connection->end_handle);
48812879e75SMatthias Ringwald #endif
48912879e75SMatthias Ringwald                 connection->service_instances_num++;
49012879e75SMatthias Ringwald             } else {
49112879e75SMatthias Ringwald                 log_info("Found more then one Service instance.");
49212879e75SMatthias Ringwald             }
49312879e75SMatthias Ringwald             break;
49412879e75SMatthias Ringwald 
49512879e75SMatthias Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
49612879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_characteristic_query_result_get_service_id(packet));
49712879e75SMatthias Ringwald             btstack_assert(client != NULL);
49812879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_characteristic_query_result_get_connection_id(packet));
49912879e75SMatthias Ringwald             btstack_assert(connection != NULL);
50012879e75SMatthias Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
50112879e75SMatthias Ringwald 
50212879e75SMatthias Ringwald             characteristic_index = gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(client, connection, characteristic.uuid16);
50312879e75SMatthias Ringwald             if (characteristic_index < client->characteristics_desc16_num){
50412879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].value_handle = characteristic.value_handle;
50512879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].properties = characteristic.properties;
50612879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].end_handle = characteristic.end_handle;
50712879e75SMatthias Ringwald 
50812879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
50912879e75SMatthias Ringwald                 printf("    [%u] Attribute Handle 0x%04X, Properties 0x%02X, Value Handle 0x%04X, UUID 0x%04X\n",
51012879e75SMatthias Ringwald                        characteristic_index, characteristic.start_handle,
51112879e75SMatthias Ringwald                        characteristic.properties, characteristic.value_handle, characteristic.uuid16);
51212879e75SMatthias Ringwald #endif
51312879e75SMatthias Ringwald             }
51412879e75SMatthias Ringwald             break;
51512879e75SMatthias Ringwald 
51612879e75SMatthias Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
51712879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_all_characteristic_descriptors_query_result_get_service_id(packet));
51812879e75SMatthias Ringwald             btstack_assert(client != NULL);
51912879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_all_characteristic_descriptors_query_result_get_connection_id(packet));
52012879e75SMatthias Ringwald             btstack_assert(connection != NULL);
52112879e75SMatthias Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
52212879e75SMatthias Ringwald 
52312879e75SMatthias Ringwald             if (characteristic_descriptor.uuid16 != ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
52412879e75SMatthias Ringwald                 break;
52512879e75SMatthias Ringwald             }
52612879e75SMatthias Ringwald             btstack_assert(connection->state == GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT);
52712879e75SMatthias Ringwald 
52812879e75SMatthias Ringwald             if ( ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY)   != 0u) ||
52912879e75SMatthias Ringwald                  ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u)
53012879e75SMatthias Ringwald                ){
53112879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle = characteristic_descriptor.handle;
53212879e75SMatthias Ringwald             } else {
53312879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle = 0;
53412879e75SMatthias Ringwald             }
53512879e75SMatthias Ringwald             connection->characteristic_index++;
53612879e75SMatthias Ringwald 
53712879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
53812879e75SMatthias Ringwald             printf("    Characteristic Configuration Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
53912879e75SMatthias Ringwald                 characteristic_descriptor.handle,
54012879e75SMatthias Ringwald                 characteristic_descriptor.uuid16);
54112879e75SMatthias Ringwald #endif
54212879e75SMatthias Ringwald             break;
54312879e75SMatthias Ringwald 
54412879e75SMatthias Ringwald         case GATT_EVENT_QUERY_COMPLETE:
54512879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_query_complete_get_service_id(packet));
54612879e75SMatthias Ringwald             btstack_assert(client != NULL);
54712879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_query_complete_get_connection_id(packet));
54812879e75SMatthias Ringwald             btstack_assert(connection != NULL);
54912879e75SMatthias Ringwald             call_run = gatt_service_client_handle_query_complete(client, connection, gatt_event_query_complete_get_att_status(packet));
55012879e75SMatthias Ringwald             break;
55112879e75SMatthias Ringwald 
55212879e75SMatthias Ringwald         default:
55312879e75SMatthias Ringwald             call_run = false;
55412879e75SMatthias Ringwald             break;
55512879e75SMatthias Ringwald     }
55612879e75SMatthias Ringwald 
55712879e75SMatthias Ringwald     if (call_run){
55812879e75SMatthias Ringwald         gatt_service_client_run_for_client(client, connection);
55912879e75SMatthias Ringwald     }
56012879e75SMatthias Ringwald }
56112879e75SMatthias Ringwald 
56212879e75SMatthias Ringwald static void gatt_service_client_hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
56312879e75SMatthias Ringwald     UNUSED(channel);
56412879e75SMatthias Ringwald     UNUSED(size);
56512879e75SMatthias Ringwald     UNUSED(packet);
56612879e75SMatthias Ringwald     UNUSED(size);
56712879e75SMatthias Ringwald 
56812879e75SMatthias Ringwald     if (packet_type != HCI_EVENT_PACKET) return;
56912879e75SMatthias Ringwald 
57012879e75SMatthias Ringwald     hci_con_handle_t con_handle;
57112879e75SMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
57212879e75SMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
57312879e75SMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
57412879e75SMatthias Ringwald             gatt_service_client_handle_disconnect(con_handle);
57512879e75SMatthias Ringwald             break;
57612879e75SMatthias Ringwald         default:
57712879e75SMatthias Ringwald             break;
57812879e75SMatthias Ringwald     }
57912879e75SMatthias Ringwald }
58012879e75SMatthias Ringwald 
58112879e75SMatthias Ringwald /* API */
58212879e75SMatthias Ringwald 
58312879e75SMatthias Ringwald void gatt_service_client_init(void){
58412879e75SMatthias Ringwald     if (false == gatt_service_client_intitialized){
58512879e75SMatthias Ringwald         gatt_service_client_hci_callback_registration.callback = gatt_service_client_hci_event_handler;
58612879e75SMatthias Ringwald         hci_add_event_handler(&gatt_service_client_hci_callback_registration);
58712879e75SMatthias Ringwald         gatt_service_client_intitialized = true;
58812879e75SMatthias Ringwald     }
58912879e75SMatthias Ringwald }
59012879e75SMatthias Ringwald 
59112879e75SMatthias Ringwald void gatt_service_client_register_client(gatt_service_client_t *client, btstack_packet_handler_t packet_handler,
59212879e75SMatthias Ringwald                                          const uint16_t *characteristic_uuid16s, uint16_t characteristic_uuid16s_num) {
59312879e75SMatthias Ringwald 
59412879e75SMatthias Ringwald     btstack_assert(gatt_service_client_intitialized);
59512879e75SMatthias Ringwald 
59612879e75SMatthias Ringwald     gatt_service_client_service_cid = btstack_next_cid_ignoring_zero(gatt_service_client_service_cid);
59712879e75SMatthias Ringwald     client->service_id =gatt_service_client_service_cid;
59812879e75SMatthias Ringwald     client->cid_counter = 0;
59912879e75SMatthias Ringwald     client->characteristics_desc16_num = 0;
60012879e75SMatthias Ringwald     client->packet_handler = packet_handler;
60141c44321SMatthias Ringwald     client->characteristics_desc16 = characteristic_uuid16s;
60241c44321SMatthias Ringwald     client->characteristics_desc16_num = characteristic_uuid16s_num;
60312879e75SMatthias Ringwald     btstack_linked_list_add(&gatt_service_clients, &client->item);
60412879e75SMatthias Ringwald }
60512879e75SMatthias Ringwald 
60612879e75SMatthias Ringwald uint8_t
60712879e75SMatthias Ringwald gatt_service_client_connect_primary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client,
60812879e75SMatthias Ringwald                                                         gatt_service_client_connection_t *connection,
60912879e75SMatthias Ringwald                                                         uint16_t service_uuid16, uint8_t service_index,
61012879e75SMatthias Ringwald                                                         gatt_service_client_characteristic_t *characteristics,
61112879e75SMatthias Ringwald                                                         uint8_t characteristics_num) {
61212879e75SMatthias Ringwald 
61312879e75SMatthias Ringwald     btstack_assert(client          != NULL);
61412879e75SMatthias Ringwald     btstack_assert(connection      != NULL);
61512879e75SMatthias Ringwald     btstack_assert(characteristics != NULL);
61612879e75SMatthias Ringwald 
61712879e75SMatthias Ringwald     if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){
61812879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
61912879e75SMatthias Ringwald     }
62012879e75SMatthias Ringwald 
62112879e75SMatthias Ringwald     if (characteristics_num < client->characteristics_desc16_num){
62212879e75SMatthias Ringwald         log_info("At least %u characteristics needed", client->characteristics_desc16_num);
62312879e75SMatthias Ringwald         return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
62412879e75SMatthias Ringwald     }
62512879e75SMatthias Ringwald 
62612879e75SMatthias Ringwald     connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE;
62712879e75SMatthias Ringwald     connection->client              = client;
62812879e75SMatthias Ringwald     connection->cid                 = gatt_service_client_get_next_cid(client);
62912879e75SMatthias Ringwald     connection->con_handle          = con_handle;
63012879e75SMatthias Ringwald     connection->service_uuid16      = service_uuid16;
63112879e75SMatthias Ringwald     connection->service_index       = service_index;
63212879e75SMatthias Ringwald     connection->characteristics     = characteristics;
63312879e75SMatthias Ringwald     btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection);
63412879e75SMatthias Ringwald 
63512879e75SMatthias Ringwald     gatt_service_client_run_for_client(client, connection);
63612879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
63712879e75SMatthias Ringwald }
63812879e75SMatthias Ringwald 
63912879e75SMatthias Ringwald uint8_t
64012879e75SMatthias Ringwald gatt_service_client_connect_secondary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client,
64112879e75SMatthias Ringwald                                                           gatt_service_client_connection_t *connection,
64212879e75SMatthias Ringwald                                                           uint16_t service_uuid16, uint8_t service_index,
64312879e75SMatthias Ringwald                                                           uint16_t service_start_handle, uint16_t service_end_handle,
64412879e75SMatthias Ringwald                                                           gatt_service_client_characteristic_t *characteristics,
64512879e75SMatthias Ringwald                                                           uint8_t characteristics_num) {
64612879e75SMatthias Ringwald 
64712879e75SMatthias Ringwald     btstack_assert(client != NULL);
64812879e75SMatthias Ringwald     btstack_assert(connection != NULL);
64912879e75SMatthias Ringwald     btstack_assert(characteristics != NULL);
65012879e75SMatthias Ringwald     btstack_assert(characteristics_num >= client->characteristics_desc16_num);
65112879e75SMatthias Ringwald 
65212879e75SMatthias Ringwald     if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){
65312879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
65412879e75SMatthias Ringwald     }
65512879e75SMatthias Ringwald 
65612879e75SMatthias Ringwald     uint16_t cid = gatt_service_client_get_next_cid(client);
65712879e75SMatthias Ringwald 
65812879e75SMatthias Ringwald     connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
65912879e75SMatthias Ringwald     connection->client              = client;
66012879e75SMatthias Ringwald     connection->cid                 = cid;
66112879e75SMatthias Ringwald     connection->con_handle          = con_handle;
66212879e75SMatthias Ringwald     connection->service_uuid16      = service_uuid16;
66312879e75SMatthias Ringwald     connection->service_index       = service_index;
66412879e75SMatthias Ringwald     connection->start_handle        = service_start_handle;
66512879e75SMatthias Ringwald     connection->end_handle          = service_end_handle;
66612879e75SMatthias Ringwald     connection->characteristics     = characteristics;
66712879e75SMatthias Ringwald     btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection);
66812879e75SMatthias Ringwald 
66912879e75SMatthias Ringwald     gatt_service_client_run_for_client(client, connection);
67012879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
67112879e75SMatthias Ringwald }
67212879e75SMatthias Ringwald 
67312879e75SMatthias Ringwald uint8_t
67412879e75SMatthias Ringwald gatt_service_client_can_query_characteristic(const gatt_service_client_connection_t *connection,
67512879e75SMatthias Ringwald                                              uint8_t characteristic_index) {
67612879e75SMatthias Ringwald     if (connection->state != GATT_SERVICE_CLIENT_STATE_CONNECTED){
67712879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
67812879e75SMatthias Ringwald     }
67912879e75SMatthias Ringwald 
68012879e75SMatthias Ringwald     if (connection->characteristics[characteristic_index].value_handle == 0){
68112879e75SMatthias Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
68212879e75SMatthias Ringwald     }
68312879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
68412879e75SMatthias Ringwald }
68512879e75SMatthias Ringwald 
68612879e75SMatthias Ringwald uint8_t gatt_service_client_disconnect(gatt_service_client_connection_t *connection) {
68712879e75SMatthias Ringwald     // finalize connections
68812879e75SMatthias Ringwald     gatt_service_client_emit_disconnected(connection->client->packet_handler, connection->con_handle, connection->cid);
68912879e75SMatthias Ringwald     gatt_service_client_finalize_connection(connection->client, connection);
69012879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
69112879e75SMatthias Ringwald }
69212879e75SMatthias Ringwald 
69312879e75SMatthias Ringwald void gatt_service_client_unregister_client(gatt_service_client_t * client){
69412879e75SMatthias Ringwald     btstack_assert(client != NULL);
69512879e75SMatthias Ringwald 
69612879e75SMatthias Ringwald     client->packet_handler = NULL;
69712879e75SMatthias Ringwald 
69812879e75SMatthias Ringwald     client->cid_counter = 0;
69912879e75SMatthias Ringwald     client->characteristics_desc16_num = 0;
70012879e75SMatthias Ringwald 
70112879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
70212879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
70312879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
70412879e75SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
70512879e75SMatthias Ringwald         gatt_service_client_finalize_connection(client, connection);
70612879e75SMatthias Ringwald     }
70712879e75SMatthias Ringwald 
70812879e75SMatthias Ringwald     btstack_linked_list_remove(&gatt_service_clients, &client->item);
70912879e75SMatthias Ringwald }
71012879e75SMatthias Ringwald 
71112879e75SMatthias Ringwald void gatt_service_client_dump_characteristic_value_handles(const gatt_service_client_connection_t *connection,
71212879e75SMatthias Ringwald                                                            const char **characteristic_names) {
71312879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
71412879e75SMatthias Ringwald     uint8_t i;
71512879e75SMatthias Ringwald     for (i = 0; i < connection->client->characteristics_desc16_num; i++) {
71612879e75SMatthias Ringwald         printf("0x%04X %s\n", connection->characteristics[i].value_handle, characteristic_names[i]);
71712879e75SMatthias Ringwald     }
71812879e75SMatthias Ringwald #else
71912879e75SMatthias Ringwald     UNUSED(connection);
72012879e75SMatthias Ringwald     UNUSED(characteristic_names);
72112879e75SMatthias Ringwald #endif
72212879e75SMatthias Ringwald }
72312879e75SMatthias Ringwald 
72412879e75SMatthias Ringwald void gatt_service_client_deinit(void){
72512879e75SMatthias Ringwald     gatt_service_client_service_cid = 0;
72612879e75SMatthias Ringwald     gatt_service_clients = NULL;
72712879e75SMatthias Ringwald     gatt_service_client_intitialized = false;
72812879e75SMatthias Ringwald }
72912879e75SMatthias Ringwald 
730