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