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