xref: /btstack/src/ble/gatt_service_client.c (revision 41c443219031bee53db470de84d5ebf4c808d432)
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){
14512879e75SMatthias Ringwald     switch (att_error_code){
14612879e75SMatthias Ringwald         case ATT_ERROR_SUCCESS:
14712879e75SMatthias Ringwald             return ERROR_CODE_SUCCESS;
14812879e75SMatthias Ringwald         case ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH:
14912879e75SMatthias Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
15012879e75SMatthias Ringwald 
15112879e75SMatthias Ringwald         default:
15212879e75SMatthias Ringwald             log_info("ATT ERROR 0x%02x mapped to ERROR_CODE_UNSPECIFIED_ERROR", att_error_code);
15312879e75SMatthias Ringwald             return ERROR_CODE_UNSPECIFIED_ERROR;
15412879e75SMatthias Ringwald     }
15512879e75SMatthias Ringwald }
15612879e75SMatthias Ringwald 
15712879e75SMatthias 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){
15812879e75SMatthias Ringwald     btstack_assert(event_callback != NULL);
15912879e75SMatthias Ringwald 
16012879e75SMatthias Ringwald     log_info("GATT Client Helper, connected 0x%04x, status 0x%02x", con_handle, status);
16112879e75SMatthias Ringwald 
16212879e75SMatthias Ringwald     uint8_t event[9];
16312879e75SMatthias Ringwald     int pos = 0;
16412879e75SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
16512879e75SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
16612879e75SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_CONNECTED;
16712879e75SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
16812879e75SMatthias Ringwald     pos += 2;
16912879e75SMatthias Ringwald     little_endian_store_16(event, pos, cid);
17012879e75SMatthias Ringwald     pos += 2;
17112879e75SMatthias Ringwald     event[pos++] = 0; // num included services
17212879e75SMatthias Ringwald     event[pos++] = status;
17312879e75SMatthias Ringwald     (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
17412879e75SMatthias Ringwald }
17512879e75SMatthias Ringwald 
17612879e75SMatthias Ringwald static void gatt_service_client_emit_disconnected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid){
17712879e75SMatthias Ringwald     btstack_assert(event_callback != NULL);
17812879e75SMatthias Ringwald 
17912879e75SMatthias Ringwald     uint8_t event[7];
18012879e75SMatthias Ringwald     int pos = 0;
18112879e75SMatthias Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
18212879e75SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
18312879e75SMatthias Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED;
18412879e75SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
18512879e75SMatthias Ringwald     pos += 2;
18612879e75SMatthias Ringwald     little_endian_store_16(event, pos, cid);
18712879e75SMatthias Ringwald     pos += 2;
18812879e75SMatthias Ringwald     (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
18912879e75SMatthias Ringwald }
19012879e75SMatthias Ringwald 
19112879e75SMatthias Ringwald static uint16_t gatt_service_client_get_next_cid(gatt_service_client_t * client){
19212879e75SMatthias Ringwald     client->cid_counter = btstack_next_cid_ignoring_zero(client->cid_counter);
19312879e75SMatthias Ringwald     return client->cid_counter;
19412879e75SMatthias Ringwald }
19512879e75SMatthias Ringwald 
19612879e75SMatthias Ringwald static bool gatt_service_client_more_descriptor_queries(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) {
19712879e75SMatthias Ringwald     bool next_query_found = false;
19812879e75SMatthias Ringwald     while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) {
19912879e75SMatthias Ringwald         uint16_t notify_or_indicate = ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE;
20012879e75SMatthias Ringwald         if ((connection->characteristics[connection->characteristic_index].properties & notify_or_indicate) != 0u){
20112879e75SMatthias Ringwald             next_query_found = true;
20212879e75SMatthias Ringwald             break;
20312879e75SMatthias Ringwald         }
20412879e75SMatthias Ringwald         connection->characteristic_index++;
20512879e75SMatthias Ringwald     }
20612879e75SMatthias Ringwald     return next_query_found;
20712879e75SMatthias Ringwald }
20812879e75SMatthias Ringwald 
20912879e75SMatthias Ringwald static bool gatt_service_client_have_more_notifications_to_enable(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) {
21012879e75SMatthias Ringwald     bool next_query_found = false;
21112879e75SMatthias Ringwald     while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) {
21212879e75SMatthias Ringwald         if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0) {
21312879e75SMatthias Ringwald             next_query_found = true;
21412879e75SMatthias Ringwald             break;
21512879e75SMatthias Ringwald         }
21612879e75SMatthias Ringwald         connection->characteristic_index++;
21712879e75SMatthias Ringwald     }
21812879e75SMatthias Ringwald     return next_query_found;
21912879e75SMatthias Ringwald }
22012879e75SMatthias Ringwald 
22112879e75SMatthias Ringwald static uint8_t gatt_service_client_register_notification(gatt_service_client_t * client,
22212879e75SMatthias Ringwald                                                          gatt_service_client_connection_t *connection) {
22312879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
22412879e75SMatthias Ringwald     uint8_t status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
22512879e75SMatthias Ringwald 
22612879e75SMatthias Ringwald     if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0){
22712879e75SMatthias Ringwald         characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle;
22812879e75SMatthias Ringwald         characteristic.end_handle = connection->characteristics[connection->characteristic_index].end_handle;
22912879e75SMatthias Ringwald         characteristic.properties = connection->characteristics[connection->characteristic_index].properties;
23012879e75SMatthias Ringwald 
23112879e75SMatthias Ringwald         if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u){
23212879e75SMatthias Ringwald             status = gatt_client_write_client_characteristic_configuration_with_context(
23312879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
23412879e75SMatthias Ringwald                     connection->con_handle,
23512879e75SMatthias Ringwald                     &characteristic,
23612879e75SMatthias Ringwald                     GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION,
23712879e75SMatthias Ringwald                     client->service_id,
23812879e75SMatthias Ringwald                     connection->cid);
23912879e75SMatthias Ringwald         } else if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){
24012879e75SMatthias Ringwald             status = gatt_client_write_client_characteristic_configuration_with_context(
24112879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
24212879e75SMatthias Ringwald                     connection->con_handle,
24312879e75SMatthias Ringwald                     &characteristic,
24412879e75SMatthias Ringwald                     GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION,
24512879e75SMatthias Ringwald                     client->service_id,
24612879e75SMatthias Ringwald                     connection->cid);
24712879e75SMatthias Ringwald         }
24812879e75SMatthias Ringwald     }
24912879e75SMatthias Ringwald     return status;
25012879e75SMatthias Ringwald }
25112879e75SMatthias Ringwald 
25212879e75SMatthias Ringwald 
25312879e75SMatthias Ringwald static void gatt_service_client_run_for_client(gatt_service_client_t * client, gatt_service_client_connection_t * connection){
25412879e75SMatthias Ringwald     uint8_t status = ATT_ERROR_SUCCESS;
25512879e75SMatthias Ringwald     gatt_client_service_t service;
25612879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
25712879e75SMatthias Ringwald 
25812879e75SMatthias Ringwald     switch (connection->state){
25912879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE:
26012879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
26112879e75SMatthias Ringwald             status = gatt_client_discover_primary_services_by_uuid16_with_context(
26212879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
26312879e75SMatthias Ringwald                     connection->con_handle,
26412879e75SMatthias Ringwald                     connection->service_uuid16,
26512879e75SMatthias Ringwald                     client->service_id,
26612879e75SMatthias Ringwald                     connection->cid);
26712879e75SMatthias Ringwald             break;
26812879e75SMatthias Ringwald 
26912879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
27012879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
27112879e75SMatthias Ringwald             printf("Read characteristics [service 0x%04x]:\n", connection->service_uuid16);
27212879e75SMatthias Ringwald #endif
27312879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
27412879e75SMatthias Ringwald 
27512879e75SMatthias Ringwald             service.start_group_handle = connection->start_handle;
27612879e75SMatthias Ringwald             service.end_group_handle = connection->end_handle;
27712879e75SMatthias Ringwald             service.uuid16 = connection->service_uuid16;
27812879e75SMatthias Ringwald 
27912879e75SMatthias Ringwald             status = gatt_client_discover_characteristics_for_service_with_context(
28012879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
28112879e75SMatthias Ringwald                     connection->con_handle,
28212879e75SMatthias Ringwald                     &service,
28312879e75SMatthias Ringwald                     client->service_id,
28412879e75SMatthias Ringwald                     connection->cid);
28512879e75SMatthias Ringwald             break;
28612879e75SMatthias Ringwald 
28712879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS:
28812879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
28912879e75SMatthias Ringwald             printf("Read client characteristic descriptors for characteristic[%u, uuid16 0x%04x, value_handle 0x%04x]:\n",
29012879e75SMatthias Ringwald                 connection->characteristic_index,
29112879e75SMatthias Ringwald                 client->characteristics_desc16[connection->characteristic_index],
29212879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].value_handle);
29312879e75SMatthias Ringwald #endif
29412879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT;
29512879e75SMatthias Ringwald             characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle;
29612879e75SMatthias Ringwald             characteristic.properties   = connection->characteristics[connection->characteristic_index].properties;
29712879e75SMatthias Ringwald             characteristic.end_handle   = connection->characteristics[connection->characteristic_index].end_handle;
29812879e75SMatthias Ringwald 
29912879e75SMatthias Ringwald             (void) gatt_client_discover_characteristic_descriptors_with_context(
30012879e75SMatthias Ringwald                     &gatt_service_client_gatt_packet_handler,
30112879e75SMatthias Ringwald                     connection->con_handle,
30212879e75SMatthias Ringwald                     &characteristic,
30312879e75SMatthias Ringwald                     client->service_id,
30412879e75SMatthias Ringwald                     connection->cid);
30512879e75SMatthias Ringwald             break;
30612879e75SMatthias Ringwald 
30712879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION:
30812879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
30912879e75SMatthias Ringwald             if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){
31012879e75SMatthias Ringwald                 printf("Register notification for characteristic");
31112879e75SMatthias Ringwald             } else {
31212879e75SMatthias Ringwald                 printf("Register indication for characteristic");
31312879e75SMatthias Ringwald             }
31412879e75SMatthias Ringwald 
31512879e75SMatthias Ringwald             printf("[%u, uuid16 0x%04x, ccd handle 0x%04x]\n",
31612879e75SMatthias Ringwald                 connection->characteristic_index,
31712879e75SMatthias Ringwald                 client->characteristics_desc16[connection->characteristic_index],
31812879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle);
31912879e75SMatthias Ringwald #endif
32012879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED;
32112879e75SMatthias Ringwald             status = gatt_service_client_register_notification(client, connection);
32212879e75SMatthias Ringwald             connection->characteristic_index++;
32312879e75SMatthias Ringwald 
32412879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
32512879e75SMatthias Ringwald             if (status != ERROR_CODE_SUCCESS) {
32612879e75SMatthias Ringwald                 printf("Notification not supported, status 0%02X\n.", status);
32712879e75SMatthias Ringwald             }
32812879e75SMatthias Ringwald #else
32912879e75SMatthias Ringwald             UNUSED(status);
33012879e75SMatthias Ringwald #endif
33112879e75SMatthias Ringwald             return;
33212879e75SMatthias Ringwald 
33312879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_CONNECTED:
33412879e75SMatthias Ringwald             // TODO
33512879e75SMatthias Ringwald             break;
33612879e75SMatthias Ringwald         default:
33712879e75SMatthias Ringwald             break;
33812879e75SMatthias Ringwald     }
33912879e75SMatthias Ringwald 
34012879e75SMatthias Ringwald     if (status != ATT_ERROR_SUCCESS){
34112879e75SMatthias Ringwald         gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_service_client_att_status_to_error_code(status));
34212879e75SMatthias Ringwald         gatt_service_client_finalize_connection(client, connection);
34312879e75SMatthias Ringwald     }
34412879e75SMatthias Ringwald }
34512879e75SMatthias Ringwald 
34612879e75SMatthias Ringwald // @return true if client valid / run function should be called
34712879e75SMatthias Ringwald static bool gatt_service_client_handle_query_complete(gatt_service_client_t *client,
34812879e75SMatthias Ringwald                                                       gatt_service_client_connection_t *connection,
34912879e75SMatthias Ringwald                                                       uint8_t status) {
35012879e75SMatthias Ringwald     btstack_assert(client != NULL);
35112879e75SMatthias Ringwald     btstack_assert(connection != NULL);
35212879e75SMatthias Ringwald 
35312879e75SMatthias Ringwald     if (status != ATT_ERROR_SUCCESS){
35412879e75SMatthias Ringwald         switch (connection->state){
35512879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
35612879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
35712879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
35812879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
35912879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_service_client_att_status_to_error_code(status));
36012879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
36112879e75SMatthias Ringwald                 return false;
36212879e75SMatthias Ringwald             case GATT_SERVICE_CLIENT_STATE_CONNECTED:
36312879e75SMatthias Ringwald                 break;
36412879e75SMatthias Ringwald             default:
36512879e75SMatthias Ringwald                 btstack_unreachable();
36612879e75SMatthias Ringwald                 break;
36712879e75SMatthias Ringwald         }
36812879e75SMatthias Ringwald     }
36912879e75SMatthias Ringwald 
37012879e75SMatthias Ringwald     switch (connection->state){
37112879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
37212879e75SMatthias Ringwald             if (connection->service_instances_num == 0){
37312879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
37412879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
37512879e75SMatthias Ringwald                 return false;
37612879e75SMatthias Ringwald             }
37712879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
37812879e75SMatthias Ringwald             connection->characteristic_index = 0;
37912879e75SMatthias Ringwald             break;
38012879e75SMatthias Ringwald 
38112879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
38212879e75SMatthias Ringwald             connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
38312879e75SMatthias Ringwald             connection->characteristic_index = 0;
38412879e75SMatthias Ringwald             break;
38512879e75SMatthias Ringwald 
38612879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
38712879e75SMatthias Ringwald             if (gatt_service_client_more_descriptor_queries(client, connection)){
38812879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
38912879e75SMatthias Ringwald                 break;
39012879e75SMatthias Ringwald             }
39112879e75SMatthias Ringwald 
39212879e75SMatthias Ringwald             connection->characteristic_index = 0;
39312879e75SMatthias Ringwald             if (gatt_service_client_have_more_notifications_to_enable(client, connection)){
39412879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
39512879e75SMatthias Ringwald             } else {
39612879e75SMatthias Ringwald                 connection->characteristic_index = 0;
39712879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED;
39812879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS);
39912879e75SMatthias Ringwald             }
40012879e75SMatthias Ringwald             break;
40112879e75SMatthias Ringwald 
40212879e75SMatthias Ringwald         case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
40312879e75SMatthias Ringwald             if (gatt_service_client_have_more_notifications_to_enable(client, connection)){
40412879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
40512879e75SMatthias Ringwald             } else {
40612879e75SMatthias Ringwald                 // all notifications registered, start listening
40712879e75SMatthias Ringwald                 gatt_client_service_t service;
40812879e75SMatthias Ringwald                 service.start_group_handle = connection->start_handle;
40912879e75SMatthias Ringwald                 service.end_group_handle = connection->end_handle;
41012879e75SMatthias Ringwald 
41112879e75SMatthias Ringwald                 gatt_client_listen_for_service_characteristic_value_updates(&connection->notification_listener, client->packet_handler,
41212879e75SMatthias Ringwald                                                                             connection->con_handle, &service, client->service_id, connection->cid);
41312879e75SMatthias Ringwald 
41412879e75SMatthias Ringwald                 connection->characteristic_index = 0;
41512879e75SMatthias Ringwald                 connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED;
41612879e75SMatthias Ringwald                 gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS);
41712879e75SMatthias Ringwald             }
41812879e75SMatthias Ringwald 
41912879e75SMatthias Ringwald             break;
42012879e75SMatthias Ringwald 
42112879e75SMatthias Ringwald         default:
42212879e75SMatthias Ringwald             break;
42312879e75SMatthias Ringwald 
42412879e75SMatthias Ringwald     }
42512879e75SMatthias Ringwald     // TODO run_for_client
42612879e75SMatthias Ringwald     return true;
42712879e75SMatthias Ringwald }
42812879e75SMatthias Ringwald 
42912879e75SMatthias Ringwald static uint8_t gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(
43012879e75SMatthias Ringwald         gatt_service_client_t * client,
43112879e75SMatthias Ringwald         gatt_service_client_connection_t * connection,
43212879e75SMatthias Ringwald         uint16_t uuid16){
43312879e75SMatthias Ringwald 
43412879e75SMatthias Ringwald         uint8_t index = 0xff;
43512879e75SMatthias Ringwald 
43612879e75SMatthias Ringwald         uint8_t i;
43712879e75SMatthias Ringwald 
43812879e75SMatthias Ringwald         for (i = 0; i < client->characteristics_desc16_num; i++){
43912879e75SMatthias Ringwald             if (client->characteristics_desc16[i] == uuid16){
44012879e75SMatthias Ringwald                 // allow for more then one instance of the same characteristic (as in OTS client)
44112879e75SMatthias Ringwald                 if (connection->characteristics[i].value_handle != 0){
44212879e75SMatthias Ringwald                    continue;
44312879e75SMatthias Ringwald                 }
44412879e75SMatthias Ringwald                 index = i;
44512879e75SMatthias Ringwald                 break;
44612879e75SMatthias Ringwald             }
44712879e75SMatthias Ringwald         }
44812879e75SMatthias Ringwald         return index;
44912879e75SMatthias Ringwald }
45012879e75SMatthias Ringwald 
45112879e75SMatthias Ringwald static void gatt_service_client_handle_disconnect(hci_con_handle_t con_handle){
45212879e75SMatthias Ringwald     btstack_linked_list_iterator_t service_it;
45312879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&service_it, &gatt_service_clients);
45412879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&service_it)){
45512879e75SMatthias Ringwald         gatt_service_client_t * client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&service_it);
45612879e75SMatthias Ringwald         btstack_linked_list_iterator_t connection_it;
45712879e75SMatthias Ringwald         btstack_linked_list_iterator_init(&connection_it, &client->connections);
45812879e75SMatthias Ringwald         while (btstack_linked_list_iterator_has_next(&connection_it)){
45912879e75SMatthias Ringwald             gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&connection_it);
46012879e75SMatthias Ringwald             if (connection->con_handle == con_handle) {
46112879e75SMatthias Ringwald                 gatt_service_client_emit_disconnected(client->packet_handler, connection->con_handle, connection->cid);
46212879e75SMatthias Ringwald                 gatt_service_client_finalize_connection(client, connection);
46312879e75SMatthias Ringwald             }
46412879e75SMatthias Ringwald         }
46512879e75SMatthias Ringwald     }
46612879e75SMatthias Ringwald }
46712879e75SMatthias Ringwald 
46812879e75SMatthias Ringwald static void
46912879e75SMatthias Ringwald gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
47012879e75SMatthias Ringwald     UNUSED(channel);
47112879e75SMatthias Ringwald     UNUSED(size);
47212879e75SMatthias Ringwald 
47312879e75SMatthias Ringwald     if (packet_type != HCI_EVENT_PACKET) return;
47412879e75SMatthias Ringwald 
47512879e75SMatthias Ringwald     gatt_service_client_t * client;
47612879e75SMatthias Ringwald     gatt_service_client_connection_t * connection;
47712879e75SMatthias Ringwald     gatt_client_service_t service;
47812879e75SMatthias Ringwald     gatt_client_characteristic_t characteristic;
47912879e75SMatthias Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
48012879e75SMatthias Ringwald     uint8_t characteristic_index;
48112879e75SMatthias Ringwald 
48212879e75SMatthias Ringwald     bool call_run = true;
48312879e75SMatthias Ringwald     switch (hci_event_packet_get_type(packet)){
48412879e75SMatthias Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
48512879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_service_query_result_get_service_id(packet));
48612879e75SMatthias Ringwald             btstack_assert(client != NULL);
48712879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_service_query_result_get_connection_id(packet));
48812879e75SMatthias Ringwald             btstack_assert(connection != NULL);
48912879e75SMatthias Ringwald 
49012879e75SMatthias Ringwald             if (connection->service_instances_num < 1){
49112879e75SMatthias Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
49212879e75SMatthias Ringwald                 connection->start_handle = service.start_group_handle;
49312879e75SMatthias Ringwald                 connection->end_handle   = service.end_group_handle;
49412879e75SMatthias Ringwald 
49512879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
49612879e75SMatthias Ringwald                 printf("Service: start handle 0x%04X, end handle 0x%04X\n", connection->start_handle, connection->end_handle);
49712879e75SMatthias Ringwald #endif
49812879e75SMatthias Ringwald                 connection->service_instances_num++;
49912879e75SMatthias Ringwald             } else {
50012879e75SMatthias Ringwald                 log_info("Found more then one Service instance.");
50112879e75SMatthias Ringwald             }
50212879e75SMatthias Ringwald             break;
50312879e75SMatthias Ringwald 
50412879e75SMatthias Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
50512879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_characteristic_query_result_get_service_id(packet));
50612879e75SMatthias Ringwald             btstack_assert(client != NULL);
50712879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_characteristic_query_result_get_connection_id(packet));
50812879e75SMatthias Ringwald             btstack_assert(connection != NULL);
50912879e75SMatthias Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
51012879e75SMatthias Ringwald 
51112879e75SMatthias Ringwald             characteristic_index = gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(client, connection, characteristic.uuid16);
51212879e75SMatthias Ringwald             if (characteristic_index < client->characteristics_desc16_num){
51312879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].value_handle = characteristic.value_handle;
51412879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].properties = characteristic.properties;
51512879e75SMatthias Ringwald                 connection->characteristics[characteristic_index].end_handle = characteristic.end_handle;
51612879e75SMatthias Ringwald 
51712879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
51812879e75SMatthias Ringwald                 printf("    [%u] Attribute Handle 0x%04X, Properties 0x%02X, Value Handle 0x%04X, UUID 0x%04X\n",
51912879e75SMatthias Ringwald                        characteristic_index, characteristic.start_handle,
52012879e75SMatthias Ringwald                        characteristic.properties, characteristic.value_handle, characteristic.uuid16);
52112879e75SMatthias Ringwald #endif
52212879e75SMatthias Ringwald             }
52312879e75SMatthias Ringwald             break;
52412879e75SMatthias Ringwald 
52512879e75SMatthias Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
52612879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_all_characteristic_descriptors_query_result_get_service_id(packet));
52712879e75SMatthias Ringwald             btstack_assert(client != NULL);
52812879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_all_characteristic_descriptors_query_result_get_connection_id(packet));
52912879e75SMatthias Ringwald             btstack_assert(connection != NULL);
53012879e75SMatthias Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
53112879e75SMatthias Ringwald 
53212879e75SMatthias Ringwald             if (characteristic_descriptor.uuid16 != ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
53312879e75SMatthias Ringwald                 break;
53412879e75SMatthias Ringwald             }
53512879e75SMatthias Ringwald             btstack_assert(connection->state == GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT);
53612879e75SMatthias Ringwald 
53712879e75SMatthias Ringwald             if ( ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY)   != 0u) ||
53812879e75SMatthias Ringwald                  ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u)
53912879e75SMatthias Ringwald                ){
54012879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle = characteristic_descriptor.handle;
54112879e75SMatthias Ringwald             } else {
54212879e75SMatthias Ringwald                 connection->characteristics[connection->characteristic_index].client_configuration_handle = 0;
54312879e75SMatthias Ringwald             }
54412879e75SMatthias Ringwald             connection->characteristic_index++;
54512879e75SMatthias Ringwald 
54612879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
54712879e75SMatthias Ringwald             printf("    Characteristic Configuration Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
54812879e75SMatthias Ringwald                 characteristic_descriptor.handle,
54912879e75SMatthias Ringwald                 characteristic_descriptor.uuid16);
55012879e75SMatthias Ringwald #endif
55112879e75SMatthias Ringwald             break;
55212879e75SMatthias Ringwald 
55312879e75SMatthias Ringwald         case GATT_EVENT_QUERY_COMPLETE:
55412879e75SMatthias Ringwald             client = gatt_service_client_get_service_client_for_id(gatt_event_query_complete_get_service_id(packet));
55512879e75SMatthias Ringwald             btstack_assert(client != NULL);
55612879e75SMatthias Ringwald             connection = gatt_service_client_get_connection_for_cid(client, gatt_event_query_complete_get_connection_id(packet));
55712879e75SMatthias Ringwald             btstack_assert(connection != NULL);
55812879e75SMatthias Ringwald             call_run = gatt_service_client_handle_query_complete(client, connection, gatt_event_query_complete_get_att_status(packet));
55912879e75SMatthias Ringwald             break;
56012879e75SMatthias Ringwald 
56112879e75SMatthias Ringwald         default:
56212879e75SMatthias Ringwald             call_run = false;
56312879e75SMatthias Ringwald             break;
56412879e75SMatthias Ringwald     }
56512879e75SMatthias Ringwald 
56612879e75SMatthias Ringwald     if (call_run){
56712879e75SMatthias Ringwald         gatt_service_client_run_for_client(client, connection);
56812879e75SMatthias Ringwald     }
56912879e75SMatthias Ringwald }
57012879e75SMatthias Ringwald 
57112879e75SMatthias Ringwald static void gatt_service_client_hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
57212879e75SMatthias Ringwald     UNUSED(channel);
57312879e75SMatthias Ringwald     UNUSED(size);
57412879e75SMatthias Ringwald     UNUSED(packet);
57512879e75SMatthias Ringwald     UNUSED(size);
57612879e75SMatthias Ringwald 
57712879e75SMatthias Ringwald     if (packet_type != HCI_EVENT_PACKET) return;
57812879e75SMatthias Ringwald 
57912879e75SMatthias Ringwald     hci_con_handle_t con_handle;
58012879e75SMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
58112879e75SMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
58212879e75SMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
58312879e75SMatthias Ringwald             gatt_service_client_handle_disconnect(con_handle);
58412879e75SMatthias Ringwald             break;
58512879e75SMatthias Ringwald         default:
58612879e75SMatthias Ringwald             break;
58712879e75SMatthias Ringwald     }
58812879e75SMatthias Ringwald }
58912879e75SMatthias Ringwald 
59012879e75SMatthias Ringwald /* API */
59112879e75SMatthias Ringwald 
59212879e75SMatthias Ringwald void gatt_service_client_init(void){
59312879e75SMatthias Ringwald     if (false == gatt_service_client_intitialized){
59412879e75SMatthias Ringwald         gatt_service_client_hci_callback_registration.callback = gatt_service_client_hci_event_handler;
59512879e75SMatthias Ringwald         hci_add_event_handler(&gatt_service_client_hci_callback_registration);
59612879e75SMatthias Ringwald         gatt_service_client_intitialized = true;
59712879e75SMatthias Ringwald     }
59812879e75SMatthias Ringwald }
59912879e75SMatthias Ringwald 
60012879e75SMatthias Ringwald void gatt_service_client_register_client(gatt_service_client_t *client, btstack_packet_handler_t packet_handler,
60112879e75SMatthias Ringwald                                          const uint16_t *characteristic_uuid16s, uint16_t characteristic_uuid16s_num) {
60212879e75SMatthias Ringwald 
60312879e75SMatthias Ringwald     btstack_assert(gatt_service_client_intitialized);
60412879e75SMatthias Ringwald 
60512879e75SMatthias Ringwald     gatt_service_client_service_cid = btstack_next_cid_ignoring_zero(gatt_service_client_service_cid);
60612879e75SMatthias Ringwald     client->service_id =gatt_service_client_service_cid;
60712879e75SMatthias Ringwald     client->cid_counter = 0;
60812879e75SMatthias Ringwald     client->characteristics_desc16_num = 0;
60912879e75SMatthias Ringwald     client->packet_handler = packet_handler;
610*41c44321SMatthias Ringwald     client->characteristics_desc16 = characteristic_uuid16s;
611*41c44321SMatthias Ringwald     client->characteristics_desc16_num = characteristic_uuid16s_num;
61212879e75SMatthias Ringwald     btstack_linked_list_add(&gatt_service_clients, &client->item);
61312879e75SMatthias Ringwald }
61412879e75SMatthias Ringwald 
61512879e75SMatthias Ringwald uint8_t
61612879e75SMatthias Ringwald gatt_service_client_connect_primary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client,
61712879e75SMatthias Ringwald                                                         gatt_service_client_connection_t *connection,
61812879e75SMatthias Ringwald                                                         uint16_t service_uuid16, uint8_t service_index,
61912879e75SMatthias Ringwald                                                         gatt_service_client_characteristic_t *characteristics,
62012879e75SMatthias Ringwald                                                         uint8_t characteristics_num) {
62112879e75SMatthias Ringwald 
62212879e75SMatthias Ringwald     btstack_assert(client          != NULL);
62312879e75SMatthias Ringwald     btstack_assert(connection      != NULL);
62412879e75SMatthias Ringwald     btstack_assert(characteristics != NULL);
62512879e75SMatthias Ringwald 
62612879e75SMatthias Ringwald     if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){
62712879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
62812879e75SMatthias Ringwald     }
62912879e75SMatthias Ringwald 
63012879e75SMatthias Ringwald     if (characteristics_num < client->characteristics_desc16_num){
63112879e75SMatthias Ringwald         log_info("At least %u characteristics needed", client->characteristics_desc16_num);
63212879e75SMatthias Ringwald         return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
63312879e75SMatthias Ringwald     }
63412879e75SMatthias Ringwald 
63512879e75SMatthias Ringwald     connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE;
63612879e75SMatthias Ringwald     connection->client              = client;
63712879e75SMatthias Ringwald     connection->cid                 = gatt_service_client_get_next_cid(client);
63812879e75SMatthias Ringwald     connection->con_handle          = con_handle;
63912879e75SMatthias Ringwald     connection->service_uuid16      = service_uuid16;
64012879e75SMatthias Ringwald     connection->service_index       = service_index;
64112879e75SMatthias Ringwald     connection->characteristics     = characteristics;
64212879e75SMatthias Ringwald     btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection);
64312879e75SMatthias Ringwald 
64412879e75SMatthias Ringwald     gatt_service_client_run_for_client(client, connection);
64512879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
64612879e75SMatthias Ringwald }
64712879e75SMatthias Ringwald 
64812879e75SMatthias Ringwald uint8_t
64912879e75SMatthias Ringwald gatt_service_client_connect_secondary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client,
65012879e75SMatthias Ringwald                                                           gatt_service_client_connection_t *connection,
65112879e75SMatthias Ringwald                                                           uint16_t service_uuid16, uint8_t service_index,
65212879e75SMatthias Ringwald                                                           uint16_t service_start_handle, uint16_t service_end_handle,
65312879e75SMatthias Ringwald                                                           gatt_service_client_characteristic_t *characteristics,
65412879e75SMatthias Ringwald                                                           uint8_t characteristics_num) {
65512879e75SMatthias Ringwald 
65612879e75SMatthias Ringwald     btstack_assert(client != NULL);
65712879e75SMatthias Ringwald     btstack_assert(connection != NULL);
65812879e75SMatthias Ringwald     btstack_assert(characteristics != NULL);
65912879e75SMatthias Ringwald     btstack_assert(characteristics_num >= client->characteristics_desc16_num);
66012879e75SMatthias Ringwald 
66112879e75SMatthias Ringwald     if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){
66212879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
66312879e75SMatthias Ringwald     }
66412879e75SMatthias Ringwald 
66512879e75SMatthias Ringwald     uint16_t cid = gatt_service_client_get_next_cid(client);
66612879e75SMatthias Ringwald 
66712879e75SMatthias Ringwald     connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
66812879e75SMatthias Ringwald     connection->client              = client;
66912879e75SMatthias Ringwald     connection->cid                 = cid;
67012879e75SMatthias Ringwald     connection->con_handle          = con_handle;
67112879e75SMatthias Ringwald     connection->service_uuid16      = service_uuid16;
67212879e75SMatthias Ringwald     connection->service_index       = service_index;
67312879e75SMatthias Ringwald     connection->start_handle        = service_start_handle;
67412879e75SMatthias Ringwald     connection->end_handle          = service_end_handle;
67512879e75SMatthias Ringwald     connection->characteristics     = characteristics;
67612879e75SMatthias Ringwald     btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection);
67712879e75SMatthias Ringwald 
67812879e75SMatthias Ringwald     gatt_service_client_run_for_client(client, connection);
67912879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
68012879e75SMatthias Ringwald }
68112879e75SMatthias Ringwald 
68212879e75SMatthias Ringwald uint8_t
68312879e75SMatthias Ringwald gatt_service_client_can_query_characteristic(const gatt_service_client_connection_t *connection,
68412879e75SMatthias Ringwald                                              uint8_t characteristic_index) {
68512879e75SMatthias Ringwald     if (connection->state != GATT_SERVICE_CLIENT_STATE_CONNECTED){
68612879e75SMatthias Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
68712879e75SMatthias Ringwald     }
68812879e75SMatthias Ringwald 
68912879e75SMatthias Ringwald     if (connection->characteristics[characteristic_index].value_handle == 0){
69012879e75SMatthias Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
69112879e75SMatthias Ringwald     }
69212879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
69312879e75SMatthias Ringwald }
69412879e75SMatthias Ringwald 
69512879e75SMatthias Ringwald uint8_t gatt_service_client_disconnect(gatt_service_client_connection_t *connection) {
69612879e75SMatthias Ringwald     // finalize connections
69712879e75SMatthias Ringwald     gatt_service_client_emit_disconnected(connection->client->packet_handler, connection->con_handle, connection->cid);
69812879e75SMatthias Ringwald     gatt_service_client_finalize_connection(connection->client, connection);
69912879e75SMatthias Ringwald     return ERROR_CODE_SUCCESS;
70012879e75SMatthias Ringwald }
70112879e75SMatthias Ringwald 
70212879e75SMatthias Ringwald void gatt_service_client_unregister_client(gatt_service_client_t * client){
70312879e75SMatthias Ringwald     btstack_assert(client != NULL);
70412879e75SMatthias Ringwald 
70512879e75SMatthias Ringwald     client->packet_handler = NULL;
70612879e75SMatthias Ringwald 
70712879e75SMatthias Ringwald     client->cid_counter = 0;
70812879e75SMatthias Ringwald     client->characteristics_desc16_num = 0;
70912879e75SMatthias Ringwald 
71012879e75SMatthias Ringwald     btstack_linked_list_iterator_t it;
71112879e75SMatthias Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections);
71212879e75SMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
71312879e75SMatthias Ringwald         gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it);
71412879e75SMatthias Ringwald         gatt_service_client_finalize_connection(client, connection);
71512879e75SMatthias Ringwald     }
71612879e75SMatthias Ringwald 
71712879e75SMatthias Ringwald     btstack_linked_list_remove(&gatt_service_clients, &client->item);
71812879e75SMatthias Ringwald }
71912879e75SMatthias Ringwald 
72012879e75SMatthias Ringwald void gatt_service_client_dump_characteristic_value_handles(const gatt_service_client_connection_t *connection,
72112879e75SMatthias Ringwald                                                            const char **characteristic_names) {
72212879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
72312879e75SMatthias Ringwald     uint8_t i;
72412879e75SMatthias Ringwald     for (i = 0; i < connection->client->characteristics_desc16_num; i++) {
72512879e75SMatthias Ringwald         printf("0x%04X %s\n", connection->characteristics[i].value_handle, characteristic_names[i]);
72612879e75SMatthias Ringwald     }
72712879e75SMatthias Ringwald #else
72812879e75SMatthias Ringwald     UNUSED(connection);
72912879e75SMatthias Ringwald     UNUSED(characteristic_names);
73012879e75SMatthias Ringwald #endif
73112879e75SMatthias Ringwald }
73212879e75SMatthias Ringwald 
73312879e75SMatthias Ringwald void gatt_service_client_deinit(void){
73412879e75SMatthias Ringwald     gatt_service_client_service_cid = 0;
73512879e75SMatthias Ringwald     gatt_service_clients = NULL;
73612879e75SMatthias Ringwald     gatt_service_client_intitialized = false;
73712879e75SMatthias Ringwald }
73812879e75SMatthias Ringwald 
739