1*12879e75SMatthias Ringwald /* 2*12879e75SMatthias Ringwald * Copyright (C) 2023 BlueKitchen GmbH 3*12879e75SMatthias Ringwald * 4*12879e75SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*12879e75SMatthias Ringwald * modification, are permitted provided that the following conditions 6*12879e75SMatthias Ringwald * are met: 7*12879e75SMatthias Ringwald * 8*12879e75SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*12879e75SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*12879e75SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*12879e75SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*12879e75SMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*12879e75SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*12879e75SMatthias Ringwald * contributors may be used to endorse or promote products derived 15*12879e75SMatthias Ringwald * from this software without specific prior written permission. 16*12879e75SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*12879e75SMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*12879e75SMatthias Ringwald * monetary gain. 19*12879e75SMatthias Ringwald * 20*12879e75SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*12879e75SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*12879e75SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*12879e75SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24*12879e75SMatthias Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*12879e75SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*12879e75SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*12879e75SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*12879e75SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*12879e75SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*12879e75SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*12879e75SMatthias Ringwald * SUCH DAMAGE. 32*12879e75SMatthias Ringwald * 33*12879e75SMatthias Ringwald * Please inquire about commercial licensing options at 34*12879e75SMatthias Ringwald * [email protected] 35*12879e75SMatthias Ringwald * 36*12879e75SMatthias Ringwald */ 37*12879e75SMatthias Ringwald 38*12879e75SMatthias Ringwald #define BTSTACK_FILE__ "gatt_service_client_helper.c" 39*12879e75SMatthias Ringwald 40*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 41*12879e75SMatthias Ringwald #include <stdio.h> 42*12879e75SMatthias Ringwald #endif 43*12879e75SMatthias Ringwald 44*12879e75SMatthias Ringwald #include <stdint.h> 45*12879e75SMatthias Ringwald #include <string.h> 46*12879e75SMatthias Ringwald 47*12879e75SMatthias Ringwald #include "ble/gatt_service_client.h" 48*12879e75SMatthias Ringwald 49*12879e75SMatthias Ringwald #include "bluetooth_gatt.h" 50*12879e75SMatthias Ringwald #include "btstack_debug.h" 51*12879e75SMatthias Ringwald #include "btstack_event.h" 52*12879e75SMatthias Ringwald 53*12879e75SMatthias Ringwald static void gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 54*12879e75SMatthias Ringwald 55*12879e75SMatthias Ringwald static bool gatt_service_client_intitialized = false; 56*12879e75SMatthias Ringwald static uint16_t gatt_service_client_service_cid; 57*12879e75SMatthias Ringwald static btstack_linked_list_t gatt_service_clients; 58*12879e75SMatthias Ringwald static btstack_packet_callback_registration_t gatt_service_client_hci_callback_registration; 59*12879e75SMatthias Ringwald 60*12879e75SMatthias Ringwald // LE Audio Service Client helper functions 61*12879e75SMatthias Ringwald static void gatt_service_client_finalize_connection(gatt_service_client_t * client, gatt_service_client_connection_t * connection){ 62*12879e75SMatthias Ringwald btstack_assert(client != NULL); 63*12879e75SMatthias Ringwald btstack_linked_list_remove(&client->connections, (btstack_linked_item_t*) connection); 64*12879e75SMatthias Ringwald } 65*12879e75SMatthias Ringwald 66*12879e75SMatthias Ringwald static gatt_service_client_t * gatt_service_client_get_service_client_for_id(uint16_t service_cid){ 67*12879e75SMatthias Ringwald btstack_linked_list_iterator_t it; 68*12879e75SMatthias Ringwald btstack_linked_list_iterator_init(&it, &gatt_service_clients); 69*12879e75SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 70*12879e75SMatthias Ringwald gatt_service_client_t * service_client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&it); 71*12879e75SMatthias Ringwald if (service_client->service_id == service_cid) { 72*12879e75SMatthias Ringwald return service_client; 73*12879e75SMatthias Ringwald }; 74*12879e75SMatthias Ringwald } 75*12879e75SMatthias Ringwald return NULL; 76*12879e75SMatthias Ringwald } 77*12879e75SMatthias Ringwald 78*12879e75SMatthias 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){ 79*12879e75SMatthias Ringwald btstack_linked_list_iterator_t it; 80*12879e75SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections); 81*12879e75SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 82*12879e75SMatthias Ringwald gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it); 83*12879e75SMatthias Ringwald if (connection->con_handle != con_handle) continue; 84*12879e75SMatthias Ringwald if (connection->service_index != service_index) continue; 85*12879e75SMatthias Ringwald return connection; 86*12879e75SMatthias Ringwald } 87*12879e75SMatthias Ringwald return NULL; 88*12879e75SMatthias Ringwald } 89*12879e75SMatthias Ringwald 90*12879e75SMatthias Ringwald static gatt_service_client_connection_t * gatt_service_client_get_connection_for_cid( 91*12879e75SMatthias Ringwald const gatt_service_client_t *client, uint16_t connection_cid){ 92*12879e75SMatthias Ringwald btstack_linked_list_iterator_t it; 93*12879e75SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections); 94*12879e75SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 95*12879e75SMatthias Ringwald gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it); 96*12879e75SMatthias Ringwald if (connection->cid != connection_cid) continue; 97*12879e75SMatthias Ringwald return connection; 98*12879e75SMatthias Ringwald } 99*12879e75SMatthias Ringwald return NULL; 100*12879e75SMatthias Ringwald } 101*12879e75SMatthias Ringwald 102*12879e75SMatthias Ringwald uint16_t gatt_service_client_get_mtu(const gatt_service_client_connection_t *connection) { 103*12879e75SMatthias Ringwald uint16_t mtu = 0; 104*12879e75SMatthias Ringwald gatt_client_get_mtu(connection->con_handle, &mtu); 105*12879e75SMatthias Ringwald return mtu; 106*12879e75SMatthias Ringwald } 107*12879e75SMatthias Ringwald 108*12879e75SMatthias Ringwald uint16_t gatt_service_client_get_connection_id(const gatt_service_client_connection_t * connection){ 109*12879e75SMatthias Ringwald return connection->cid; 110*12879e75SMatthias Ringwald 111*12879e75SMatthias Ringwald } 112*12879e75SMatthias Ringwald 113*12879e75SMatthias Ringwald hci_con_handle_t gatt_service_client_get_con_handle(const gatt_service_client_connection_t * connection){ 114*12879e75SMatthias Ringwald return connection->con_handle; 115*12879e75SMatthias Ringwald } 116*12879e75SMatthias Ringwald 117*12879e75SMatthias Ringwald uint8_t gatt_service_client_get_service_index(const gatt_service_client_connection_t * connection){ 118*12879e75SMatthias Ringwald return connection->service_index; 119*12879e75SMatthias Ringwald } 120*12879e75SMatthias Ringwald 121*12879e75SMatthias Ringwald uint16_t gatt_service_client_characteristic_uuid16_for_index(const gatt_service_client_t * client, uint8_t characteristic_index){ 122*12879e75SMatthias Ringwald if (characteristic_index < client->characteristics_desc16_num){ 123*12879e75SMatthias Ringwald return client->characteristics_desc16[characteristic_index]; 124*12879e75SMatthias Ringwald } else { 125*12879e75SMatthias Ringwald return 0; 126*12879e75SMatthias Ringwald } 127*12879e75SMatthias Ringwald } 128*12879e75SMatthias Ringwald 129*12879e75SMatthias Ringwald uint16_t gatt_service_client_characteristic_value_handle_for_index(const gatt_service_client_connection_t *connection, 130*12879e75SMatthias Ringwald uint8_t characteristic_index) { 131*12879e75SMatthias Ringwald return connection->characteristics[characteristic_index].value_handle; 132*12879e75SMatthias Ringwald } 133*12879e75SMatthias Ringwald 134*12879e75SMatthias Ringwald uint8_t gatt_service_client_characteristic_index_for_value_handle(const gatt_service_client_connection_t *connection, 135*12879e75SMatthias Ringwald uint16_t value_handle) { 136*12879e75SMatthias Ringwald for (int i = 0; i < connection->client->characteristics_desc16_num; i++){ 137*12879e75SMatthias Ringwald if (connection->characteristics[i].value_handle == value_handle) { 138*12879e75SMatthias Ringwald return i; 139*12879e75SMatthias Ringwald } 140*12879e75SMatthias Ringwald } 141*12879e75SMatthias Ringwald return GATT_SERVICE_CLIENT_INVALID_INDEX; 142*12879e75SMatthias Ringwald } 143*12879e75SMatthias Ringwald 144*12879e75SMatthias Ringwald uint8_t gatt_service_client_att_status_to_error_code(uint8_t att_error_code){ 145*12879e75SMatthias Ringwald switch (att_error_code){ 146*12879e75SMatthias Ringwald case ATT_ERROR_SUCCESS: 147*12879e75SMatthias Ringwald return ERROR_CODE_SUCCESS; 148*12879e75SMatthias Ringwald case ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH: 149*12879e75SMatthias Ringwald return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; 150*12879e75SMatthias Ringwald 151*12879e75SMatthias Ringwald default: 152*12879e75SMatthias Ringwald log_info("ATT ERROR 0x%02x mapped to ERROR_CODE_UNSPECIFIED_ERROR", att_error_code); 153*12879e75SMatthias Ringwald return ERROR_CODE_UNSPECIFIED_ERROR; 154*12879e75SMatthias Ringwald } 155*12879e75SMatthias Ringwald } 156*12879e75SMatthias Ringwald 157*12879e75SMatthias 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){ 158*12879e75SMatthias Ringwald btstack_assert(event_callback != NULL); 159*12879e75SMatthias Ringwald 160*12879e75SMatthias Ringwald log_info("GATT Client Helper, connected 0x%04x, status 0x%02x", con_handle, status); 161*12879e75SMatthias Ringwald 162*12879e75SMatthias Ringwald uint8_t event[9]; 163*12879e75SMatthias Ringwald int pos = 0; 164*12879e75SMatthias Ringwald event[pos++] = HCI_EVENT_GATTSERVICE_META; 165*12879e75SMatthias Ringwald event[pos++] = sizeof(event) - 2; 166*12879e75SMatthias Ringwald event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_CONNECTED; 167*12879e75SMatthias Ringwald little_endian_store_16(event, pos, con_handle); 168*12879e75SMatthias Ringwald pos += 2; 169*12879e75SMatthias Ringwald little_endian_store_16(event, pos, cid); 170*12879e75SMatthias Ringwald pos += 2; 171*12879e75SMatthias Ringwald event[pos++] = 0; // num included services 172*12879e75SMatthias Ringwald event[pos++] = status; 173*12879e75SMatthias Ringwald (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 174*12879e75SMatthias Ringwald } 175*12879e75SMatthias Ringwald 176*12879e75SMatthias Ringwald static void gatt_service_client_emit_disconnected(btstack_packet_handler_t event_callback, hci_con_handle_t con_handle, uint16_t cid){ 177*12879e75SMatthias Ringwald btstack_assert(event_callback != NULL); 178*12879e75SMatthias Ringwald 179*12879e75SMatthias Ringwald uint8_t event[7]; 180*12879e75SMatthias Ringwald int pos = 0; 181*12879e75SMatthias Ringwald event[pos++] = HCI_EVENT_GATTSERVICE_META; 182*12879e75SMatthias Ringwald event[pos++] = sizeof(event) - 2; 183*12879e75SMatthias Ringwald event[pos++] = GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED; 184*12879e75SMatthias Ringwald little_endian_store_16(event, pos, con_handle); 185*12879e75SMatthias Ringwald pos += 2; 186*12879e75SMatthias Ringwald little_endian_store_16(event, pos, cid); 187*12879e75SMatthias Ringwald pos += 2; 188*12879e75SMatthias Ringwald (*event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 189*12879e75SMatthias Ringwald } 190*12879e75SMatthias Ringwald 191*12879e75SMatthias Ringwald static uint16_t gatt_service_client_get_next_cid(gatt_service_client_t * client){ 192*12879e75SMatthias Ringwald client->cid_counter = btstack_next_cid_ignoring_zero(client->cid_counter); 193*12879e75SMatthias Ringwald return client->cid_counter; 194*12879e75SMatthias Ringwald } 195*12879e75SMatthias Ringwald 196*12879e75SMatthias Ringwald static bool gatt_service_client_more_descriptor_queries(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) { 197*12879e75SMatthias Ringwald bool next_query_found = false; 198*12879e75SMatthias Ringwald while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) { 199*12879e75SMatthias Ringwald uint16_t notify_or_indicate = ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE; 200*12879e75SMatthias Ringwald if ((connection->characteristics[connection->characteristic_index].properties & notify_or_indicate) != 0u){ 201*12879e75SMatthias Ringwald next_query_found = true; 202*12879e75SMatthias Ringwald break; 203*12879e75SMatthias Ringwald } 204*12879e75SMatthias Ringwald connection->characteristic_index++; 205*12879e75SMatthias Ringwald } 206*12879e75SMatthias Ringwald return next_query_found; 207*12879e75SMatthias Ringwald } 208*12879e75SMatthias Ringwald 209*12879e75SMatthias Ringwald static bool gatt_service_client_have_more_notifications_to_enable(const gatt_service_client_t * client, gatt_service_client_connection_t * connection) { 210*12879e75SMatthias Ringwald bool next_query_found = false; 211*12879e75SMatthias Ringwald while (!next_query_found && (connection->characteristic_index < client->characteristics_desc16_num)) { 212*12879e75SMatthias Ringwald if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0) { 213*12879e75SMatthias Ringwald next_query_found = true; 214*12879e75SMatthias Ringwald break; 215*12879e75SMatthias Ringwald } 216*12879e75SMatthias Ringwald connection->characteristic_index++; 217*12879e75SMatthias Ringwald } 218*12879e75SMatthias Ringwald return next_query_found; 219*12879e75SMatthias Ringwald } 220*12879e75SMatthias Ringwald 221*12879e75SMatthias Ringwald static uint8_t gatt_service_client_register_notification(gatt_service_client_t * client, 222*12879e75SMatthias Ringwald gatt_service_client_connection_t *connection) { 223*12879e75SMatthias Ringwald gatt_client_characteristic_t characteristic; 224*12879e75SMatthias Ringwald uint8_t status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 225*12879e75SMatthias Ringwald 226*12879e75SMatthias Ringwald if (connection->characteristics[connection->characteristic_index].client_configuration_handle != 0){ 227*12879e75SMatthias Ringwald characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle; 228*12879e75SMatthias Ringwald characteristic.end_handle = connection->characteristics[connection->characteristic_index].end_handle; 229*12879e75SMatthias Ringwald characteristic.properties = connection->characteristics[connection->characteristic_index].properties; 230*12879e75SMatthias Ringwald 231*12879e75SMatthias Ringwald if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u){ 232*12879e75SMatthias Ringwald status = gatt_client_write_client_characteristic_configuration_with_context( 233*12879e75SMatthias Ringwald &gatt_service_client_gatt_packet_handler, 234*12879e75SMatthias Ringwald connection->con_handle, 235*12879e75SMatthias Ringwald &characteristic, 236*12879e75SMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION, 237*12879e75SMatthias Ringwald client->service_id, 238*12879e75SMatthias Ringwald connection->cid); 239*12879e75SMatthias Ringwald } else if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){ 240*12879e75SMatthias Ringwald status = gatt_client_write_client_characteristic_configuration_with_context( 241*12879e75SMatthias Ringwald &gatt_service_client_gatt_packet_handler, 242*12879e75SMatthias Ringwald connection->con_handle, 243*12879e75SMatthias Ringwald &characteristic, 244*12879e75SMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION, 245*12879e75SMatthias Ringwald client->service_id, 246*12879e75SMatthias Ringwald connection->cid); 247*12879e75SMatthias Ringwald } 248*12879e75SMatthias Ringwald } 249*12879e75SMatthias Ringwald return status; 250*12879e75SMatthias Ringwald } 251*12879e75SMatthias Ringwald 252*12879e75SMatthias Ringwald 253*12879e75SMatthias Ringwald static void gatt_service_client_run_for_client(gatt_service_client_t * client, gatt_service_client_connection_t * connection){ 254*12879e75SMatthias Ringwald uint8_t status = ATT_ERROR_SUCCESS; 255*12879e75SMatthias Ringwald gatt_client_service_t service; 256*12879e75SMatthias Ringwald gatt_client_characteristic_t characteristic; 257*12879e75SMatthias Ringwald 258*12879e75SMatthias Ringwald switch (connection->state){ 259*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE: 260*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT; 261*12879e75SMatthias Ringwald status = gatt_client_discover_primary_services_by_uuid16_with_context( 262*12879e75SMatthias Ringwald &gatt_service_client_gatt_packet_handler, 263*12879e75SMatthias Ringwald connection->con_handle, 264*12879e75SMatthias Ringwald connection->service_uuid16, 265*12879e75SMatthias Ringwald client->service_id, 266*12879e75SMatthias Ringwald connection->cid); 267*12879e75SMatthias Ringwald break; 268*12879e75SMatthias Ringwald 269*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS: 270*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 271*12879e75SMatthias Ringwald printf("Read characteristics [service 0x%04x]:\n", connection->service_uuid16); 272*12879e75SMatthias Ringwald #endif 273*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT; 274*12879e75SMatthias Ringwald 275*12879e75SMatthias Ringwald service.start_group_handle = connection->start_handle; 276*12879e75SMatthias Ringwald service.end_group_handle = connection->end_handle; 277*12879e75SMatthias Ringwald service.uuid16 = connection->service_uuid16; 278*12879e75SMatthias Ringwald 279*12879e75SMatthias Ringwald status = gatt_client_discover_characteristics_for_service_with_context( 280*12879e75SMatthias Ringwald &gatt_service_client_gatt_packet_handler, 281*12879e75SMatthias Ringwald connection->con_handle, 282*12879e75SMatthias Ringwald &service, 283*12879e75SMatthias Ringwald client->service_id, 284*12879e75SMatthias Ringwald connection->cid); 285*12879e75SMatthias Ringwald break; 286*12879e75SMatthias Ringwald 287*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS: 288*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 289*12879e75SMatthias Ringwald printf("Read client characteristic descriptors for characteristic[%u, uuid16 0x%04x, value_handle 0x%04x]:\n", 290*12879e75SMatthias Ringwald connection->characteristic_index, 291*12879e75SMatthias Ringwald client->characteristics_desc16[connection->characteristic_index], 292*12879e75SMatthias Ringwald connection->characteristics[connection->characteristic_index].value_handle); 293*12879e75SMatthias Ringwald #endif 294*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT; 295*12879e75SMatthias Ringwald characteristic.value_handle = connection->characteristics[connection->characteristic_index].value_handle; 296*12879e75SMatthias Ringwald characteristic.properties = connection->characteristics[connection->characteristic_index].properties; 297*12879e75SMatthias Ringwald characteristic.end_handle = connection->characteristics[connection->characteristic_index].end_handle; 298*12879e75SMatthias Ringwald 299*12879e75SMatthias Ringwald (void) gatt_client_discover_characteristic_descriptors_with_context( 300*12879e75SMatthias Ringwald &gatt_service_client_gatt_packet_handler, 301*12879e75SMatthias Ringwald connection->con_handle, 302*12879e75SMatthias Ringwald &characteristic, 303*12879e75SMatthias Ringwald client->service_id, 304*12879e75SMatthias Ringwald connection->cid); 305*12879e75SMatthias Ringwald break; 306*12879e75SMatthias Ringwald 307*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION: 308*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 309*12879e75SMatthias Ringwald if ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u){ 310*12879e75SMatthias Ringwald printf("Register notification for characteristic"); 311*12879e75SMatthias Ringwald } else { 312*12879e75SMatthias Ringwald printf("Register indication for characteristic"); 313*12879e75SMatthias Ringwald } 314*12879e75SMatthias Ringwald 315*12879e75SMatthias Ringwald printf("[%u, uuid16 0x%04x, ccd handle 0x%04x]\n", 316*12879e75SMatthias Ringwald connection->characteristic_index, 317*12879e75SMatthias Ringwald client->characteristics_desc16[connection->characteristic_index], 318*12879e75SMatthias Ringwald connection->characteristics[connection->characteristic_index].client_configuration_handle); 319*12879e75SMatthias Ringwald #endif 320*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED; 321*12879e75SMatthias Ringwald status = gatt_service_client_register_notification(client, connection); 322*12879e75SMatthias Ringwald connection->characteristic_index++; 323*12879e75SMatthias Ringwald 324*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 325*12879e75SMatthias Ringwald if (status != ERROR_CODE_SUCCESS) { 326*12879e75SMatthias Ringwald printf("Notification not supported, status 0%02X\n.", status); 327*12879e75SMatthias Ringwald } 328*12879e75SMatthias Ringwald #else 329*12879e75SMatthias Ringwald UNUSED(status); 330*12879e75SMatthias Ringwald #endif 331*12879e75SMatthias Ringwald return; 332*12879e75SMatthias Ringwald 333*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_CONNECTED: 334*12879e75SMatthias Ringwald // TODO 335*12879e75SMatthias Ringwald break; 336*12879e75SMatthias Ringwald default: 337*12879e75SMatthias Ringwald break; 338*12879e75SMatthias Ringwald } 339*12879e75SMatthias Ringwald 340*12879e75SMatthias Ringwald if (status != ATT_ERROR_SUCCESS){ 341*12879e75SMatthias Ringwald gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_service_client_att_status_to_error_code(status)); 342*12879e75SMatthias Ringwald gatt_service_client_finalize_connection(client, connection); 343*12879e75SMatthias Ringwald } 344*12879e75SMatthias Ringwald } 345*12879e75SMatthias Ringwald 346*12879e75SMatthias Ringwald // @return true if client valid / run function should be called 347*12879e75SMatthias Ringwald static bool gatt_service_client_handle_query_complete(gatt_service_client_t *client, 348*12879e75SMatthias Ringwald gatt_service_client_connection_t *connection, 349*12879e75SMatthias Ringwald uint8_t status) { 350*12879e75SMatthias Ringwald btstack_assert(client != NULL); 351*12879e75SMatthias Ringwald btstack_assert(connection != NULL); 352*12879e75SMatthias Ringwald 353*12879e75SMatthias Ringwald if (status != ATT_ERROR_SUCCESS){ 354*12879e75SMatthias Ringwald switch (connection->state){ 355*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 356*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 357*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT: 358*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED: 359*12879e75SMatthias Ringwald gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, gatt_service_client_att_status_to_error_code(status)); 360*12879e75SMatthias Ringwald gatt_service_client_finalize_connection(client, connection); 361*12879e75SMatthias Ringwald return false; 362*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_CONNECTED: 363*12879e75SMatthias Ringwald break; 364*12879e75SMatthias Ringwald default: 365*12879e75SMatthias Ringwald btstack_unreachable(); 366*12879e75SMatthias Ringwald break; 367*12879e75SMatthias Ringwald } 368*12879e75SMatthias Ringwald } 369*12879e75SMatthias Ringwald 370*12879e75SMatthias Ringwald switch (connection->state){ 371*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT: 372*12879e75SMatthias Ringwald if (connection->service_instances_num == 0){ 373*12879e75SMatthias Ringwald gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 374*12879e75SMatthias Ringwald gatt_service_client_finalize_connection(client, connection); 375*12879e75SMatthias Ringwald return false; 376*12879e75SMatthias Ringwald } 377*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS; 378*12879e75SMatthias Ringwald connection->characteristic_index = 0; 379*12879e75SMatthias Ringwald break; 380*12879e75SMatthias Ringwald 381*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT: 382*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS; 383*12879e75SMatthias Ringwald connection->characteristic_index = 0; 384*12879e75SMatthias Ringwald break; 385*12879e75SMatthias Ringwald 386*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT: 387*12879e75SMatthias Ringwald if (gatt_service_client_more_descriptor_queries(client, connection)){ 388*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS; 389*12879e75SMatthias Ringwald break; 390*12879e75SMatthias Ringwald } 391*12879e75SMatthias Ringwald 392*12879e75SMatthias Ringwald connection->characteristic_index = 0; 393*12879e75SMatthias Ringwald if (gatt_service_client_have_more_notifications_to_enable(client, connection)){ 394*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION; 395*12879e75SMatthias Ringwald } else { 396*12879e75SMatthias Ringwald connection->characteristic_index = 0; 397*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED; 398*12879e75SMatthias Ringwald gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS); 399*12879e75SMatthias Ringwald } 400*12879e75SMatthias Ringwald break; 401*12879e75SMatthias Ringwald 402*12879e75SMatthias Ringwald case GATT_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED: 403*12879e75SMatthias Ringwald if (gatt_service_client_have_more_notifications_to_enable(client, connection)){ 404*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION; 405*12879e75SMatthias Ringwald } else { 406*12879e75SMatthias Ringwald // all notifications registered, start listening 407*12879e75SMatthias Ringwald gatt_client_service_t service; 408*12879e75SMatthias Ringwald service.start_group_handle = connection->start_handle; 409*12879e75SMatthias Ringwald service.end_group_handle = connection->end_handle; 410*12879e75SMatthias Ringwald 411*12879e75SMatthias Ringwald gatt_client_listen_for_service_characteristic_value_updates(&connection->notification_listener, client->packet_handler, 412*12879e75SMatthias Ringwald connection->con_handle, &service, client->service_id, connection->cid); 413*12879e75SMatthias Ringwald 414*12879e75SMatthias Ringwald connection->characteristic_index = 0; 415*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_CONNECTED; 416*12879e75SMatthias Ringwald gatt_service_client_emit_connected(client->packet_handler, connection->con_handle, connection->cid, ERROR_CODE_SUCCESS); 417*12879e75SMatthias Ringwald } 418*12879e75SMatthias Ringwald 419*12879e75SMatthias Ringwald break; 420*12879e75SMatthias Ringwald 421*12879e75SMatthias Ringwald default: 422*12879e75SMatthias Ringwald break; 423*12879e75SMatthias Ringwald 424*12879e75SMatthias Ringwald } 425*12879e75SMatthias Ringwald // TODO run_for_client 426*12879e75SMatthias Ringwald return true; 427*12879e75SMatthias Ringwald } 428*12879e75SMatthias Ringwald 429*12879e75SMatthias Ringwald static uint8_t gatt_service_client_get_uninitialized_characteristic_index_for_uuid16( 430*12879e75SMatthias Ringwald gatt_service_client_t * client, 431*12879e75SMatthias Ringwald gatt_service_client_connection_t * connection, 432*12879e75SMatthias Ringwald uint16_t uuid16){ 433*12879e75SMatthias Ringwald 434*12879e75SMatthias Ringwald uint8_t index = 0xff; 435*12879e75SMatthias Ringwald 436*12879e75SMatthias Ringwald uint8_t i; 437*12879e75SMatthias Ringwald 438*12879e75SMatthias Ringwald for (i = 0; i < client->characteristics_desc16_num; i++){ 439*12879e75SMatthias Ringwald if (client->characteristics_desc16[i] == uuid16){ 440*12879e75SMatthias Ringwald // allow for more then one instance of the same characteristic (as in OTS client) 441*12879e75SMatthias Ringwald if (connection->characteristics[i].value_handle != 0){ 442*12879e75SMatthias Ringwald continue; 443*12879e75SMatthias Ringwald } 444*12879e75SMatthias Ringwald index = i; 445*12879e75SMatthias Ringwald break; 446*12879e75SMatthias Ringwald } 447*12879e75SMatthias Ringwald } 448*12879e75SMatthias Ringwald return index; 449*12879e75SMatthias Ringwald } 450*12879e75SMatthias Ringwald 451*12879e75SMatthias Ringwald static void gatt_service_client_handle_disconnect(hci_con_handle_t con_handle){ 452*12879e75SMatthias Ringwald btstack_linked_list_iterator_t service_it; 453*12879e75SMatthias Ringwald btstack_linked_list_iterator_init(&service_it, &gatt_service_clients); 454*12879e75SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&service_it)){ 455*12879e75SMatthias Ringwald gatt_service_client_t * client = (gatt_service_client_t *)btstack_linked_list_iterator_next(&service_it); 456*12879e75SMatthias Ringwald btstack_linked_list_iterator_t connection_it; 457*12879e75SMatthias Ringwald btstack_linked_list_iterator_init(&connection_it, &client->connections); 458*12879e75SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&connection_it)){ 459*12879e75SMatthias Ringwald gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&connection_it); 460*12879e75SMatthias Ringwald if (connection->con_handle == con_handle) { 461*12879e75SMatthias Ringwald gatt_service_client_emit_disconnected(client->packet_handler, connection->con_handle, connection->cid); 462*12879e75SMatthias Ringwald gatt_service_client_finalize_connection(client, connection); 463*12879e75SMatthias Ringwald } 464*12879e75SMatthias Ringwald } 465*12879e75SMatthias Ringwald } 466*12879e75SMatthias Ringwald } 467*12879e75SMatthias Ringwald 468*12879e75SMatthias Ringwald static void 469*12879e75SMatthias Ringwald gatt_service_client_gatt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 470*12879e75SMatthias Ringwald UNUSED(channel); 471*12879e75SMatthias Ringwald UNUSED(size); 472*12879e75SMatthias Ringwald 473*12879e75SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 474*12879e75SMatthias Ringwald 475*12879e75SMatthias Ringwald gatt_service_client_t * client; 476*12879e75SMatthias Ringwald gatt_service_client_connection_t * connection; 477*12879e75SMatthias Ringwald gatt_client_service_t service; 478*12879e75SMatthias Ringwald gatt_client_characteristic_t characteristic; 479*12879e75SMatthias Ringwald gatt_client_characteristic_descriptor_t characteristic_descriptor; 480*12879e75SMatthias Ringwald uint8_t characteristic_index; 481*12879e75SMatthias Ringwald 482*12879e75SMatthias Ringwald bool call_run = true; 483*12879e75SMatthias Ringwald switch (hci_event_packet_get_type(packet)){ 484*12879e75SMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT: 485*12879e75SMatthias Ringwald client = gatt_service_client_get_service_client_for_id(gatt_event_service_query_result_get_service_id(packet)); 486*12879e75SMatthias Ringwald btstack_assert(client != NULL); 487*12879e75SMatthias Ringwald connection = gatt_service_client_get_connection_for_cid(client, gatt_event_service_query_result_get_connection_id(packet)); 488*12879e75SMatthias Ringwald btstack_assert(connection != NULL); 489*12879e75SMatthias Ringwald 490*12879e75SMatthias Ringwald if (connection->service_instances_num < 1){ 491*12879e75SMatthias Ringwald gatt_event_service_query_result_get_service(packet, &service); 492*12879e75SMatthias Ringwald connection->start_handle = service.start_group_handle; 493*12879e75SMatthias Ringwald connection->end_handle = service.end_group_handle; 494*12879e75SMatthias Ringwald 495*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 496*12879e75SMatthias Ringwald printf("Service: start handle 0x%04X, end handle 0x%04X\n", connection->start_handle, connection->end_handle); 497*12879e75SMatthias Ringwald #endif 498*12879e75SMatthias Ringwald connection->service_instances_num++; 499*12879e75SMatthias Ringwald } else { 500*12879e75SMatthias Ringwald log_info("Found more then one Service instance."); 501*12879e75SMatthias Ringwald } 502*12879e75SMatthias Ringwald break; 503*12879e75SMatthias Ringwald 504*12879e75SMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 505*12879e75SMatthias Ringwald client = gatt_service_client_get_service_client_for_id(gatt_event_characteristic_query_result_get_service_id(packet)); 506*12879e75SMatthias Ringwald btstack_assert(client != NULL); 507*12879e75SMatthias Ringwald connection = gatt_service_client_get_connection_for_cid(client, gatt_event_characteristic_query_result_get_connection_id(packet)); 508*12879e75SMatthias Ringwald btstack_assert(connection != NULL); 509*12879e75SMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 510*12879e75SMatthias Ringwald 511*12879e75SMatthias Ringwald characteristic_index = gatt_service_client_get_uninitialized_characteristic_index_for_uuid16(client, connection, characteristic.uuid16); 512*12879e75SMatthias Ringwald if (characteristic_index < client->characteristics_desc16_num){ 513*12879e75SMatthias Ringwald connection->characteristics[characteristic_index].value_handle = characteristic.value_handle; 514*12879e75SMatthias Ringwald connection->characteristics[characteristic_index].properties = characteristic.properties; 515*12879e75SMatthias Ringwald connection->characteristics[characteristic_index].end_handle = characteristic.end_handle; 516*12879e75SMatthias Ringwald 517*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 518*12879e75SMatthias Ringwald printf(" [%u] Attribute Handle 0x%04X, Properties 0x%02X, Value Handle 0x%04X, UUID 0x%04X\n", 519*12879e75SMatthias Ringwald characteristic_index, characteristic.start_handle, 520*12879e75SMatthias Ringwald characteristic.properties, characteristic.value_handle, characteristic.uuid16); 521*12879e75SMatthias Ringwald #endif 522*12879e75SMatthias Ringwald } 523*12879e75SMatthias Ringwald break; 524*12879e75SMatthias Ringwald 525*12879e75SMatthias Ringwald case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: 526*12879e75SMatthias Ringwald client = gatt_service_client_get_service_client_for_id(gatt_event_all_characteristic_descriptors_query_result_get_service_id(packet)); 527*12879e75SMatthias Ringwald btstack_assert(client != NULL); 528*12879e75SMatthias Ringwald connection = gatt_service_client_get_connection_for_cid(client, gatt_event_all_characteristic_descriptors_query_result_get_connection_id(packet)); 529*12879e75SMatthias Ringwald btstack_assert(connection != NULL); 530*12879e75SMatthias Ringwald gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor); 531*12879e75SMatthias Ringwald 532*12879e75SMatthias Ringwald if (characteristic_descriptor.uuid16 != ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){ 533*12879e75SMatthias Ringwald break; 534*12879e75SMatthias Ringwald } 535*12879e75SMatthias Ringwald btstack_assert(connection->state == GATT_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT); 536*12879e75SMatthias Ringwald 537*12879e75SMatthias Ringwald if ( ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_NOTIFY) != 0u) || 538*12879e75SMatthias Ringwald ((connection->characteristics[connection->characteristic_index].properties & ATT_PROPERTY_INDICATE) != 0u) 539*12879e75SMatthias Ringwald ){ 540*12879e75SMatthias Ringwald connection->characteristics[connection->characteristic_index].client_configuration_handle = characteristic_descriptor.handle; 541*12879e75SMatthias Ringwald } else { 542*12879e75SMatthias Ringwald connection->characteristics[connection->characteristic_index].client_configuration_handle = 0; 543*12879e75SMatthias Ringwald } 544*12879e75SMatthias Ringwald connection->characteristic_index++; 545*12879e75SMatthias Ringwald 546*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 547*12879e75SMatthias Ringwald printf(" Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n", 548*12879e75SMatthias Ringwald characteristic_descriptor.handle, 549*12879e75SMatthias Ringwald characteristic_descriptor.uuid16); 550*12879e75SMatthias Ringwald #endif 551*12879e75SMatthias Ringwald break; 552*12879e75SMatthias Ringwald 553*12879e75SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 554*12879e75SMatthias Ringwald client = gatt_service_client_get_service_client_for_id(gatt_event_query_complete_get_service_id(packet)); 555*12879e75SMatthias Ringwald btstack_assert(client != NULL); 556*12879e75SMatthias Ringwald connection = gatt_service_client_get_connection_for_cid(client, gatt_event_query_complete_get_connection_id(packet)); 557*12879e75SMatthias Ringwald btstack_assert(connection != NULL); 558*12879e75SMatthias Ringwald call_run = gatt_service_client_handle_query_complete(client, connection, gatt_event_query_complete_get_att_status(packet)); 559*12879e75SMatthias Ringwald break; 560*12879e75SMatthias Ringwald 561*12879e75SMatthias Ringwald default: 562*12879e75SMatthias Ringwald call_run = false; 563*12879e75SMatthias Ringwald break; 564*12879e75SMatthias Ringwald } 565*12879e75SMatthias Ringwald 566*12879e75SMatthias Ringwald if (call_run){ 567*12879e75SMatthias Ringwald gatt_service_client_run_for_client(client, connection); 568*12879e75SMatthias Ringwald } 569*12879e75SMatthias Ringwald } 570*12879e75SMatthias Ringwald 571*12879e75SMatthias Ringwald static void gatt_service_client_hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 572*12879e75SMatthias Ringwald UNUSED(channel); 573*12879e75SMatthias Ringwald UNUSED(size); 574*12879e75SMatthias Ringwald UNUSED(packet); 575*12879e75SMatthias Ringwald UNUSED(size); 576*12879e75SMatthias Ringwald 577*12879e75SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 578*12879e75SMatthias Ringwald 579*12879e75SMatthias Ringwald hci_con_handle_t con_handle; 580*12879e75SMatthias Ringwald switch (hci_event_packet_get_type(packet)) { 581*12879e75SMatthias Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE: 582*12879e75SMatthias Ringwald con_handle = hci_event_disconnection_complete_get_connection_handle(packet); 583*12879e75SMatthias Ringwald gatt_service_client_handle_disconnect(con_handle); 584*12879e75SMatthias Ringwald break; 585*12879e75SMatthias Ringwald default: 586*12879e75SMatthias Ringwald break; 587*12879e75SMatthias Ringwald } 588*12879e75SMatthias Ringwald } 589*12879e75SMatthias Ringwald 590*12879e75SMatthias Ringwald /* API */ 591*12879e75SMatthias Ringwald 592*12879e75SMatthias Ringwald void gatt_service_client_init(void){ 593*12879e75SMatthias Ringwald if (false == gatt_service_client_intitialized){ 594*12879e75SMatthias Ringwald gatt_service_client_hci_callback_registration.callback = gatt_service_client_hci_event_handler; 595*12879e75SMatthias Ringwald hci_add_event_handler(&gatt_service_client_hci_callback_registration); 596*12879e75SMatthias Ringwald gatt_service_client_intitialized = true; 597*12879e75SMatthias Ringwald } 598*12879e75SMatthias Ringwald } 599*12879e75SMatthias Ringwald 600*12879e75SMatthias Ringwald void gatt_service_client_register_client(gatt_service_client_t *client, btstack_packet_handler_t packet_handler, 601*12879e75SMatthias Ringwald const uint16_t *characteristic_uuid16s, uint16_t characteristic_uuid16s_num) { 602*12879e75SMatthias Ringwald 603*12879e75SMatthias Ringwald btstack_assert(gatt_service_client_intitialized); 604*12879e75SMatthias Ringwald 605*12879e75SMatthias Ringwald gatt_service_client_service_cid = btstack_next_cid_ignoring_zero(gatt_service_client_service_cid); 606*12879e75SMatthias Ringwald client->service_id =gatt_service_client_service_cid; 607*12879e75SMatthias Ringwald client->cid_counter = 0; 608*12879e75SMatthias Ringwald client->characteristics_desc16_num = 0; 609*12879e75SMatthias Ringwald client->packet_handler = packet_handler; 610*12879e75SMatthias Ringwald btstack_linked_list_add(&gatt_service_clients, &client->item); 611*12879e75SMatthias Ringwald } 612*12879e75SMatthias Ringwald 613*12879e75SMatthias Ringwald uint8_t 614*12879e75SMatthias Ringwald gatt_service_client_connect_primary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client, 615*12879e75SMatthias Ringwald gatt_service_client_connection_t *connection, 616*12879e75SMatthias Ringwald uint16_t service_uuid16, uint8_t service_index, 617*12879e75SMatthias Ringwald gatt_service_client_characteristic_t *characteristics, 618*12879e75SMatthias Ringwald uint8_t characteristics_num) { 619*12879e75SMatthias Ringwald 620*12879e75SMatthias Ringwald btstack_assert(client != NULL); 621*12879e75SMatthias Ringwald btstack_assert(connection != NULL); 622*12879e75SMatthias Ringwald btstack_assert(characteristics != NULL); 623*12879e75SMatthias Ringwald 624*12879e75SMatthias Ringwald if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){ 625*12879e75SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 626*12879e75SMatthias Ringwald } 627*12879e75SMatthias Ringwald 628*12879e75SMatthias Ringwald if (characteristics_num < client->characteristics_desc16_num){ 629*12879e75SMatthias Ringwald log_info("At least %u characteristics needed", client->characteristics_desc16_num); 630*12879e75SMatthias Ringwald return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 631*12879e75SMatthias Ringwald } 632*12879e75SMatthias Ringwald 633*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_PRIMARY_SERVICE; 634*12879e75SMatthias Ringwald connection->client = client; 635*12879e75SMatthias Ringwald connection->cid = gatt_service_client_get_next_cid(client); 636*12879e75SMatthias Ringwald connection->con_handle = con_handle; 637*12879e75SMatthias Ringwald connection->service_uuid16 = service_uuid16; 638*12879e75SMatthias Ringwald connection->service_index = service_index; 639*12879e75SMatthias Ringwald connection->characteristics = characteristics; 640*12879e75SMatthias Ringwald btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection); 641*12879e75SMatthias Ringwald 642*12879e75SMatthias Ringwald gatt_service_client_run_for_client(client, connection); 643*12879e75SMatthias Ringwald return ERROR_CODE_SUCCESS; 644*12879e75SMatthias Ringwald } 645*12879e75SMatthias Ringwald 646*12879e75SMatthias Ringwald uint8_t 647*12879e75SMatthias Ringwald gatt_service_client_connect_secondary_service_with_uuid16(hci_con_handle_t con_handle, gatt_service_client_t *client, 648*12879e75SMatthias Ringwald gatt_service_client_connection_t *connection, 649*12879e75SMatthias Ringwald uint16_t service_uuid16, uint8_t service_index, 650*12879e75SMatthias Ringwald uint16_t service_start_handle, uint16_t service_end_handle, 651*12879e75SMatthias Ringwald gatt_service_client_characteristic_t *characteristics, 652*12879e75SMatthias Ringwald uint8_t characteristics_num) { 653*12879e75SMatthias Ringwald 654*12879e75SMatthias Ringwald btstack_assert(client != NULL); 655*12879e75SMatthias Ringwald btstack_assert(connection != NULL); 656*12879e75SMatthias Ringwald btstack_assert(characteristics != NULL); 657*12879e75SMatthias Ringwald btstack_assert(characteristics_num >= client->characteristics_desc16_num); 658*12879e75SMatthias Ringwald 659*12879e75SMatthias Ringwald if (gatt_service_client_get_connection_for_con_handle_and_service_index(client, con_handle, service_index) != NULL){ 660*12879e75SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 661*12879e75SMatthias Ringwald } 662*12879e75SMatthias Ringwald 663*12879e75SMatthias Ringwald uint16_t cid = gatt_service_client_get_next_cid(client); 664*12879e75SMatthias Ringwald 665*12879e75SMatthias Ringwald connection->state = GATT_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS; 666*12879e75SMatthias Ringwald connection->client = client; 667*12879e75SMatthias Ringwald connection->cid = cid; 668*12879e75SMatthias Ringwald connection->con_handle = con_handle; 669*12879e75SMatthias Ringwald connection->service_uuid16 = service_uuid16; 670*12879e75SMatthias Ringwald connection->service_index = service_index; 671*12879e75SMatthias Ringwald connection->start_handle = service_start_handle; 672*12879e75SMatthias Ringwald connection->end_handle = service_end_handle; 673*12879e75SMatthias Ringwald connection->characteristics = characteristics; 674*12879e75SMatthias Ringwald btstack_linked_list_add(&client->connections, (btstack_linked_item_t *) connection); 675*12879e75SMatthias Ringwald 676*12879e75SMatthias Ringwald gatt_service_client_run_for_client(client, connection); 677*12879e75SMatthias Ringwald return ERROR_CODE_SUCCESS; 678*12879e75SMatthias Ringwald } 679*12879e75SMatthias Ringwald 680*12879e75SMatthias Ringwald uint8_t 681*12879e75SMatthias Ringwald gatt_service_client_can_query_characteristic(const gatt_service_client_connection_t *connection, 682*12879e75SMatthias Ringwald uint8_t characteristic_index) { 683*12879e75SMatthias Ringwald if (connection->state != GATT_SERVICE_CLIENT_STATE_CONNECTED){ 684*12879e75SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 685*12879e75SMatthias Ringwald } 686*12879e75SMatthias Ringwald 687*12879e75SMatthias Ringwald if (connection->characteristics[characteristic_index].value_handle == 0){ 688*12879e75SMatthias Ringwald return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 689*12879e75SMatthias Ringwald } 690*12879e75SMatthias Ringwald return ERROR_CODE_SUCCESS; 691*12879e75SMatthias Ringwald } 692*12879e75SMatthias Ringwald 693*12879e75SMatthias Ringwald uint8_t gatt_service_client_disconnect(gatt_service_client_connection_t *connection) { 694*12879e75SMatthias Ringwald // finalize connections 695*12879e75SMatthias Ringwald gatt_service_client_emit_disconnected(connection->client->packet_handler, connection->con_handle, connection->cid); 696*12879e75SMatthias Ringwald gatt_service_client_finalize_connection(connection->client, connection); 697*12879e75SMatthias Ringwald return ERROR_CODE_SUCCESS; 698*12879e75SMatthias Ringwald } 699*12879e75SMatthias Ringwald 700*12879e75SMatthias Ringwald void gatt_service_client_unregister_client(gatt_service_client_t * client){ 701*12879e75SMatthias Ringwald btstack_assert(client != NULL); 702*12879e75SMatthias Ringwald 703*12879e75SMatthias Ringwald client->packet_handler = NULL; 704*12879e75SMatthias Ringwald 705*12879e75SMatthias Ringwald client->cid_counter = 0; 706*12879e75SMatthias Ringwald client->characteristics_desc16_num = 0; 707*12879e75SMatthias Ringwald 708*12879e75SMatthias Ringwald btstack_linked_list_iterator_t it; 709*12879e75SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &client->connections); 710*12879e75SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 711*12879e75SMatthias Ringwald gatt_service_client_connection_t * connection = (gatt_service_client_connection_t *)btstack_linked_list_iterator_next(&it); 712*12879e75SMatthias Ringwald gatt_service_client_finalize_connection(client, connection); 713*12879e75SMatthias Ringwald } 714*12879e75SMatthias Ringwald 715*12879e75SMatthias Ringwald btstack_linked_list_remove(&gatt_service_clients, &client->item); 716*12879e75SMatthias Ringwald } 717*12879e75SMatthias Ringwald 718*12879e75SMatthias Ringwald void gatt_service_client_dump_characteristic_value_handles(const gatt_service_client_connection_t *connection, 719*12879e75SMatthias Ringwald const char **characteristic_names) { 720*12879e75SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 721*12879e75SMatthias Ringwald uint8_t i; 722*12879e75SMatthias Ringwald for (i = 0; i < connection->client->characteristics_desc16_num; i++) { 723*12879e75SMatthias Ringwald printf("0x%04X %s\n", connection->characteristics[i].value_handle, characteristic_names[i]); 724*12879e75SMatthias Ringwald } 725*12879e75SMatthias Ringwald #else 726*12879e75SMatthias Ringwald UNUSED(connection); 727*12879e75SMatthias Ringwald UNUSED(characteristic_names); 728*12879e75SMatthias Ringwald #endif 729*12879e75SMatthias Ringwald } 730*12879e75SMatthias Ringwald 731*12879e75SMatthias Ringwald void gatt_service_client_deinit(void){ 732*12879e75SMatthias Ringwald gatt_service_client_service_cid = 0; 733*12879e75SMatthias Ringwald gatt_service_clients = NULL; 734*12879e75SMatthias Ringwald gatt_service_client_intitialized = false; 735*12879e75SMatthias Ringwald } 736*12879e75SMatthias Ringwald 737