btstack/src/ble/gatt_client.c
2023-06-19 17:33:30 +02:00

3375 lines
152 KiB
C

/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "gatt_client.c"
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include "btstack_config.h"
#include "ble/att_dispatch.h"
#include "ble/att_db.h"
#include "ble/gatt_client.h"
#include "ble/le_device_db.h"
#include "ble/sm.h"
#include "btstack_debug.h"
#include "btstack_event.h"
#include "btstack_memory.h"
#include "btstack_run_loop.h"
#include "btstack_util.h"
#include "hci.h"
#include "hci_dump.h"
#include "l2cap.h"
#include "classic/sdp_client.h"
#include "bluetooth_gatt.h"
#include "bluetooth_sdp.h"
#include "classic/sdp_util.h"
static btstack_linked_list_t gatt_client_connections;
static btstack_linked_list_t gatt_client_value_listeners;
static btstack_packet_callback_registration_t hci_event_callback_registration;
static btstack_packet_callback_registration_t sm_event_callback_registration;
// GATT Client Configuration
static bool gatt_client_mtu_exchange_enabled;
static gap_security_level_t gatt_client_required_security_level;
static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size);
static void gatt_client_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void gatt_client_report_error_if_pending(gatt_client_t *gatt_client, uint8_t att_error_code);
#ifdef ENABLE_LE_SIGNED_WRITE
static void att_signed_write_handle_cmac_result(uint8_t hash[8]);
#endif
#ifdef ENABLE_GATT_OVER_EATT
static bool gatt_client_le_enhanced_handle_can_send_query(gatt_client_t * gatt_client);
#endif
void gatt_client_init(void){
gatt_client_connections = NULL;
// default configuration
gatt_client_mtu_exchange_enabled = true;
gatt_client_required_security_level = LEVEL_0;
// register for HCI Events
hci_event_callback_registration.callback = &gatt_client_event_packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
// register for SM Events
sm_event_callback_registration.callback = &gatt_client_event_packet_handler;
sm_add_event_handler(&sm_event_callback_registration);
// and ATT Client PDUs
att_dispatch_register_client(gatt_client_att_packet_handler);
}
void gatt_client_set_required_security_level(gap_security_level_t level){
gatt_client_required_security_level = level;
}
static gatt_client_t * gatt_client_for_timer(btstack_timer_source_t * ts){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &gatt_client_connections);
while (btstack_linked_list_iterator_has_next(&it)){
gatt_client_t * gatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
if (&gatt_client->gc_timeout == ts) {
return gatt_client;
}
}
return NULL;
}
static void gatt_client_timeout_handler(btstack_timer_source_t * timer){
gatt_client_t * gatt_client = gatt_client_for_timer(timer);
if (gatt_client == NULL) return;
log_info("GATT client timeout handle, handle 0x%02x", gatt_client->con_handle);
gatt_client_report_error_if_pending(gatt_client, ATT_ERROR_TIMEOUT);
}
static void gatt_client_timeout_start(gatt_client_t * gatt_client){
log_info("GATT client timeout start, handle 0x%02x", gatt_client->con_handle);
btstack_run_loop_remove_timer(&gatt_client->gc_timeout);
btstack_run_loop_set_timer_handler(&gatt_client->gc_timeout, gatt_client_timeout_handler);
btstack_run_loop_set_timer(&gatt_client->gc_timeout, 30000); // 30 seconds sm timeout
btstack_run_loop_add_timer(&gatt_client->gc_timeout);
}
static void gatt_client_timeout_stop(gatt_client_t * gatt_client){
log_info("GATT client timeout stop, handle 0x%02x", gatt_client->con_handle);
btstack_run_loop_remove_timer(&gatt_client->gc_timeout);
}
static gap_security_level_t gatt_client_le_security_level_for_connection(hci_con_handle_t con_handle){
uint8_t encryption_key_size = gap_encryption_key_size(con_handle);
if (encryption_key_size == 0) return LEVEL_0;
bool authenticated = gap_authenticated(con_handle);
if (!authenticated) return LEVEL_2;
return encryption_key_size == 16 ? LEVEL_4 : LEVEL_3;
}
static gatt_client_t * gatt_client_get_context_for_handle(uint16_t handle){
btstack_linked_item_t *it;
for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
gatt_client_t * gatt_client = (gatt_client_t *) it;
if (gatt_client->con_handle == handle){
return gatt_client;
}
}
return NULL;
}
// @return gatt_client context
// returns existing one, or tries to setup new one
static uint8_t gatt_client_provide_context_for_handle(hci_con_handle_t con_handle, gatt_client_t ** out_gatt_client){
gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
if (gatt_client != NULL){
*out_gatt_client = gatt_client;
return ERROR_CODE_SUCCESS;
}
// bail if no such hci connection
hci_connection_t * hci_connection = hci_connection_for_handle(con_handle);
if (hci_connection == NULL){
log_error("No connection for handle 0x%04x", con_handle);
*out_gatt_client = NULL;
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
gatt_client = btstack_memory_gatt_client_get();
if (gatt_client == NULL){
*out_gatt_client = NULL;
return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
}
// init state
gatt_client->bearer_type = ATT_BEARER_UNENHANCED_LE;
gatt_client->con_handle = con_handle;
gatt_client->mtu = ATT_DEFAULT_MTU;
gatt_client->security_level = gatt_client_le_security_level_for_connection(con_handle);
if (gatt_client_mtu_exchange_enabled){
gatt_client->mtu_state = SEND_MTU_EXCHANGE;
} else {
gatt_client->mtu_state = MTU_AUTO_EXCHANGE_DISABLED;
}
gatt_client->gatt_client_state = P_READY;
#ifdef ENABLE_GATT_OVER_EATT
gatt_client->eatt_state = GATT_CLIENT_EATT_IDLE;
#endif
btstack_linked_list_add(&gatt_client_connections, (btstack_linked_item_t*)gatt_client);
// get unenhanced att bearer state
if (hci_connection->att_connection.mtu_exchanged){
gatt_client->mtu = hci_connection->att_connection.mtu;
gatt_client->mtu_state = MTU_EXCHANGED;
}
*out_gatt_client = gatt_client;
return ERROR_CODE_SUCCESS;
}
static bool is_ready(gatt_client_t * gatt_client){
return gatt_client->gatt_client_state == P_READY;
}
static uint8_t gatt_client_provide_context_for_request(hci_con_handle_t con_handle, gatt_client_t ** out_gatt_client){
gatt_client_t * gatt_client = NULL;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
#ifdef ENABLE_GATT_OVER_EATT
if (gatt_client->eatt_state == GATT_CLIENT_EATT_READY){
btstack_linked_list_iterator_t it;
gatt_client_t * eatt_client = NULL;
// find free eatt client
btstack_linked_list_iterator_init(&it, &gatt_client->eatt_clients);
while (btstack_linked_list_iterator_has_next(&it)){
gatt_client_t * client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
if (client->gatt_client_state == P_READY){
eatt_client = client;
break;
}
}
if (eatt_client == NULL){
return ERROR_CODE_COMMAND_DISALLOWED;
}
gatt_client = eatt_client;
}
#endif
if (is_ready(gatt_client) == 0){
return GATT_CLIENT_IN_WRONG_STATE;
}
gatt_client_timeout_start(gatt_client);
*out_gatt_client = gatt_client;
return status;
}
int gatt_client_is_ready(hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return 0;
}
return is_ready(gatt_client) ? 1 : 0;
}
void gatt_client_mtu_enable_auto_negotiation(uint8_t enabled){
gatt_client_mtu_exchange_enabled = enabled != 0;
}
uint8_t gatt_client_get_mtu(hci_con_handle_t con_handle, uint16_t * mtu){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
if ((gatt_client->mtu_state == MTU_EXCHANGED) || (gatt_client->mtu_state == MTU_AUTO_EXCHANGE_DISABLED)){
*mtu = gatt_client->mtu;
return ERROR_CODE_SUCCESS;
}
*mtu = ATT_DEFAULT_MTU;
return GATT_CLIENT_IN_WRONG_STATE;
}
#ifdef ENABLE_GATT_OVER_CLASSIC
// TODO: re-use single buffer for all eatt channels
static uint8_t gatt_client_le_enhanced_request_buffer[512];
#endif
static uint8_t *gatt_client_reserve_request_buffer(gatt_client_t *gatt_client) {
switch (gatt_client->bearer_type){
#ifdef ENABLE_GATT_OVER_CLASSIC
case ATT_BEARER_UNENHANCED_CLASSIC:
#endif
case ATT_BEARER_UNENHANCED_LE:
l2cap_reserve_packet_buffer();
return l2cap_get_outgoing_buffer();
#ifdef ENABLE_GATT_OVER_EATT
case ATT_BEARER_ENHANCED_LE:
return gatt_client->eatt_storage_buffer;
#endif
default:
btstack_unreachable();
break;
}
return NULL;
}
// precondition: can_send_packet_now == TRUE
static uint8_t gatt_client_send(gatt_client_t * gatt_client, uint16_t len){
switch (gatt_client->bearer_type){
case ATT_BEARER_UNENHANCED_LE:
return l2cap_send_prepared_connectionless(gatt_client->con_handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, len);
#ifdef ENABLE_GATT_OVER_CLASSIC
case ATT_BEARER_UNENHANCED_CLASSIC:
return l2cap_send_prepared(gatt_client->l2cap_cid, len);
#endif
#ifdef ENABLE_GATT_OVER_EATT
case ATT_BEARER_ENHANCED_LE:
return l2cap_send(gatt_client->l2cap_cid, gatt_client->eatt_storage_buffer, len);
#endif
default:
btstack_unreachable();
return ERROR_CODE_HARDWARE_FAILURE;
}
}
// precondition: can_send_packet_now == TRUE
static uint8_t att_confirmation(gatt_client_t * gatt_client) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = ATT_HANDLE_VALUE_CONFIRMATION;
return gatt_client_send(gatt_client, 1);
}
// precondition: can_send_packet_now == TRUE
static uint8_t att_find_information_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t start_handle,
uint16_t end_handle) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, start_handle);
little_endian_store_16(request, 3, end_handle);
return gatt_client_send(gatt_client, 5);
}
// precondition: can_send_packet_now == TRUE
static uint8_t
att_find_by_type_value_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_group_type,
uint16_t start_handle, uint16_t end_handle, uint8_t *value, uint16_t value_size) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, start_handle);
little_endian_store_16(request, 3, end_handle);
little_endian_store_16(request, 5, attribute_group_type);
(void)memcpy(&request[7], value, value_size);
return gatt_client_send(gatt_client, 7u + value_size);
}
// precondition: can_send_packet_now == TRUE
static uint8_t
att_read_by_type_or_group_request_for_uuid16(gatt_client_t *gatt_client, uint8_t request_type, uint16_t uuid16,
uint16_t start_handle, uint16_t end_handle) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, start_handle);
little_endian_store_16(request, 3, end_handle);
little_endian_store_16(request, 5, uuid16);
return gatt_client_send(gatt_client, 7);
}
// precondition: can_send_packet_now == TRUE
static uint8_t
att_read_by_type_or_group_request_for_uuid128(gatt_client_t *gatt_client, uint8_t request_type, const uint8_t *uuid128,
uint16_t start_handle, uint16_t end_handle) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, start_handle);
little_endian_store_16(request, 3, end_handle);
reverse_128(uuid128, &request[5]);
return gatt_client_send(gatt_client, 21);
}
// precondition: can_send_packet_now == TRUE
static uint8_t att_read_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, attribute_handle);
return gatt_client_send(gatt_client, 3);
}
// precondition: can_send_packet_now == TRUE
static uint8_t att_read_blob_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle,
uint16_t value_offset) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, attribute_handle);
little_endian_store_16(request, 3, value_offset);
return gatt_client_send(gatt_client, 5);
}
static uint8_t
att_read_multiple_request_with_opcode(gatt_client_t *gatt_client, uint16_t num_value_handles, uint16_t *value_handles, uint8_t opcode) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = opcode;
uint16_t i;
uint16_t offset = 1;
for (i=0;i<num_value_handles;i++){
little_endian_store_16(request, offset, value_handles[i]);
offset += 2;
}
return gatt_client_send(gatt_client, offset);
}
static uint8_t
att_read_multiple_request(gatt_client_t *gatt_client, uint16_t num_value_handles, uint16_t *value_handles) {
return att_read_multiple_request_with_opcode(gatt_client, num_value_handles, value_handles, ATT_READ_MULTIPLE_REQUEST);
}
#ifdef ENABLE_GATT_OVER_EATT
static uint8_t
att_read_multiple_variable_request(gatt_client_t *gatt_client, uint16_t num_value_handles, uint16_t *value_handles) {
return att_read_multiple_request_with_opcode(gatt_client, num_value_handles, value_handles, ATT_READ_MULTIPLE_VARIABLE_REQ);
}
#endif
#ifdef ENABLE_LE_SIGNED_WRITE
// precondition: can_send_packet_now == TRUE
static uint8_t att_signed_write_request(gatt_client_t *gatt_client, uint16_t request_type, uint16_t attribute_handle,
uint16_t value_length, uint8_t *value, uint32_t sign_counter, uint8_t sgn[8]) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, attribute_handle);
(void)memcpy(&request[3], value, value_length);
little_endian_store_32(request, 3 + value_length, sign_counter);
reverse_64(sgn, &request[3 + value_length + 4]);
return gatt_client_send(gatt_client, 3 + value_length + 12);
}
#endif
// precondition: can_send_packet_now == TRUE
static uint8_t
att_write_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle, uint16_t value_length,
uint8_t *value) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, attribute_handle);
(void)memcpy(&request[3], value, value_length);
return gatt_client_send(gatt_client, 3u + value_length);
}
// precondition: can_send_packet_now == TRUE
static uint8_t att_execute_write_request(gatt_client_t *gatt_client, uint8_t request_type, uint8_t execute_write) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
request[1] = execute_write;
return gatt_client_send(gatt_client, 2);
}
// precondition: can_send_packet_now == TRUE
static uint8_t att_prepare_write_request(gatt_client_t *gatt_client, uint8_t request_type, uint16_t attribute_handle,
uint16_t value_offset, uint16_t blob_length, uint8_t *value) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = request_type;
little_endian_store_16(request, 1, attribute_handle);
little_endian_store_16(request, 3, value_offset);
(void)memcpy(&request[5], &value[value_offset], blob_length);
return gatt_client_send(gatt_client, 5u + blob_length);
}
static uint8_t att_exchange_mtu_request(gatt_client_t *gatt_client) {
uint8_t *request = gatt_client_reserve_request_buffer(gatt_client);
request[0] = ATT_EXCHANGE_MTU_REQUEST;
uint16_t mtu = l2cap_max_le_mtu();
little_endian_store_16(request, 1, mtu);
return gatt_client_send(gatt_client, 3);
}
static uint16_t write_blob_length(gatt_client_t * gatt_client){
uint16_t max_blob_length = gatt_client->mtu - 5u;
if (gatt_client->attribute_offset >= gatt_client->attribute_length) {
return 0;
}
uint16_t rest_length = gatt_client->attribute_length - gatt_client->attribute_offset;
if (max_blob_length > rest_length){
return rest_length;
}
return max_blob_length;
}
static void send_gatt_services_request(gatt_client_t *gatt_client){
att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_GROUP_TYPE_REQUEST,
gatt_client->uuid16, gatt_client->start_group_handle,
gatt_client->end_group_handle);
}
static void send_gatt_by_uuid_request(gatt_client_t *gatt_client, uint16_t attribute_group_type){
if (gatt_client->uuid16){
uint8_t uuid16[2];
little_endian_store_16(uuid16, 0, gatt_client->uuid16);
att_find_by_type_value_request(gatt_client, ATT_FIND_BY_TYPE_VALUE_REQUEST, attribute_group_type,
gatt_client->start_group_handle, gatt_client->end_group_handle, uuid16, 2);
return;
}
uint8_t uuid128[16];
reverse_128(gatt_client->uuid128, uuid128);
att_find_by_type_value_request(gatt_client, ATT_FIND_BY_TYPE_VALUE_REQUEST, attribute_group_type,
gatt_client->start_group_handle, gatt_client->end_group_handle, uuid128, 16);
}
static void send_gatt_services_by_uuid_request(gatt_client_t *gatt_client){
send_gatt_by_uuid_request(gatt_client, GATT_PRIMARY_SERVICE_UUID);
}
static void send_gatt_included_service_uuid_request(gatt_client_t *gatt_client){
att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->query_start_handle);
}
static void send_gatt_included_service_request(gatt_client_t *gatt_client){
att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
GATT_INCLUDE_SERVICE_UUID, gatt_client->start_group_handle,
gatt_client->end_group_handle);
}
static void send_gatt_characteristic_request(gatt_client_t *gatt_client){
att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
GATT_CHARACTERISTICS_UUID, gatt_client->start_group_handle,
gatt_client->end_group_handle);
}
static void send_gatt_characteristic_descriptor_request(gatt_client_t *gatt_client){
att_find_information_request(gatt_client, ATT_FIND_INFORMATION_REQUEST, gatt_client->start_group_handle,
gatt_client->end_group_handle);
}
static void send_gatt_read_characteristic_value_request(gatt_client_t *gatt_client){
att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->attribute_handle);
}
static void send_gatt_read_by_type_request(gatt_client_t * gatt_client){
if (gatt_client->uuid16){
att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
gatt_client->uuid16, gatt_client->start_group_handle,
gatt_client->end_group_handle);
} else {
att_read_by_type_or_group_request_for_uuid128(gatt_client, ATT_READ_BY_TYPE_REQUEST,
gatt_client->uuid128, gatt_client->start_group_handle,
gatt_client->end_group_handle);
}
}
static void send_gatt_read_blob_request(gatt_client_t *gatt_client){
if (gatt_client->attribute_offset == 0){
att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->attribute_handle);
} else {
att_read_blob_request(gatt_client, ATT_READ_BLOB_REQUEST, gatt_client->attribute_handle,
gatt_client->attribute_offset);
}
}
static void send_gatt_read_multiple_request(gatt_client_t * gatt_client){
att_read_multiple_request(gatt_client, gatt_client->read_multiple_handle_count, gatt_client->read_multiple_handles);
}
#ifdef ENABLE_GATT_OVER_EATT
static void send_gatt_read_multiple_variable_request(gatt_client_t * gatt_client){
att_read_multiple_variable_request(gatt_client, gatt_client->read_multiple_handle_count, gatt_client->read_multiple_handles);
}
#endif
static void send_gatt_write_attribute_value_request(gatt_client_t * gatt_client){
att_write_request(gatt_client, ATT_WRITE_REQUEST, gatt_client->attribute_handle, gatt_client->attribute_length,
gatt_client->attribute_value);
}
static void send_gatt_write_client_characteristic_configuration_request(gatt_client_t * gatt_client){
att_write_request(gatt_client, ATT_WRITE_REQUEST, gatt_client->client_characteristic_configuration_handle, 2,
gatt_client->client_characteristic_configuration_value);
}
static void send_gatt_prepare_write_request(gatt_client_t * gatt_client){
att_prepare_write_request(gatt_client, ATT_PREPARE_WRITE_REQUEST, gatt_client->attribute_handle,
gatt_client->attribute_offset, write_blob_length(gatt_client),
gatt_client->attribute_value);
}
static void send_gatt_execute_write_request(gatt_client_t * gatt_client){
att_execute_write_request(gatt_client, ATT_EXECUTE_WRITE_REQUEST, 1);
}
static void send_gatt_cancel_prepared_write_request(gatt_client_t * gatt_client){
att_execute_write_request(gatt_client, ATT_EXECUTE_WRITE_REQUEST, 0);
}
#ifndef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
static void send_gatt_read_client_characteristic_configuration_request(gatt_client_t * gatt_client){
att_read_by_type_or_group_request_for_uuid16(gatt_client, ATT_READ_BY_TYPE_REQUEST,
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION,
gatt_client->start_group_handle, gatt_client->end_group_handle);
}
#endif
static void send_gatt_read_characteristic_descriptor_request(gatt_client_t * gatt_client){
att_read_request(gatt_client, ATT_READ_REQUEST, gatt_client->attribute_handle);
}
#ifdef ENABLE_LE_SIGNED_WRITE
static void send_gatt_signed_write_request(gatt_client_t * gatt_client, uint32_t sign_counter){
att_signed_write_request(gatt_client, ATT_SIGNED_WRITE_COMMAND, gatt_client->attribute_handle,
gatt_client->attribute_length, gatt_client->attribute_value, sign_counter,
gatt_client->cmac);
}
#endif
static uint16_t get_last_result_handle_from_service_list(uint8_t * packet, uint16_t size){
if (size < 2) return 0xffff;
uint8_t attr_length = packet[1];
if ((2 + attr_length) > size) return 0xffff;
return little_endian_read_16(packet, size - attr_length + 2u);
}
static uint16_t get_last_result_handle_from_characteristics_list(uint8_t * packet, uint16_t size){
if (size < 2) return 0xffff;
uint8_t attr_length = packet[1];
if ((2 + attr_length) > size) return 0xffff;
return little_endian_read_16(packet, size - attr_length + 3u);
}
static uint16_t get_last_result_handle_from_included_services_list(uint8_t * packet, uint16_t size){
if (size < 2) return 0xffff;
uint8_t attr_length = packet[1];
if ((2 + attr_length) > size) return 0xffff;
return little_endian_read_16(packet, size - attr_length);
}
static void gatt_client_notify_can_send_query(gatt_client_t * gatt_client){
#ifdef ENABLE_GATT_OVER_EATT
// if eatt is ready, notify all clients that can send a query
if (gatt_client->eatt_state == GATT_CLIENT_EATT_READY){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &gatt_client->eatt_clients);
while (btstack_linked_list_iterator_has_next(&it)){
gatt_client_t * client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
if (client->gatt_client_state == P_READY){
// call callback
btstack_context_callback_registration_t * callback = (btstack_context_callback_registration_t *) btstack_linked_list_pop(&gatt_client->query_requests);
if (callback == NULL) {
return;
}
(*callback->callback)(callback->context);
}
}
return;
}
#endif
while (gatt_client->gatt_client_state == P_READY){
#ifdef ENABLE_GATT_OVER_EATT
bool query_sent = gatt_client_le_enhanced_handle_can_send_query(gatt_client);
if (query_sent){
continue;
}
#endif
btstack_context_callback_registration_t * callback = (btstack_context_callback_registration_t *) btstack_linked_list_pop(&gatt_client->query_requests);
if (callback == NULL) {
return;
}
(*callback->callback)(callback->context);
}
}
// test if notification/indication should be delivered to application (BLESA)
static bool gatt_client_accept_server_message(gatt_client_t *gatt_client) {
#ifdef ENABLE_LE_PROACTIVE_AUTHENTICATION
// ignore messages until re-encryption is complete
if (gap_reconnect_security_setup_active(gatt_client->con_handle)) return false;
// after that ignore if bonded but not encrypted
return !gap_bonded(gatt_client->con_handle) || (gap_encryption_key_size(gatt_client->con_handle) > 0);
#else
UNUSED(gatt_client);
return true;
#endif
}
static void emit_event_new(btstack_packet_handler_t callback, uint8_t * packet, uint16_t size){
if (!callback) return;
hci_dump_packet(HCI_EVENT_PACKET, 1, packet, size);
(*callback)(HCI_EVENT_PACKET, 0, packet, size);
}
static void emit_event_to_registered_listeners(hci_con_handle_t con_handle, uint16_t attribute_handle, uint8_t * packet, uint16_t size){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &gatt_client_value_listeners);
while (btstack_linked_list_iterator_has_next(&it)){
gatt_client_notification_t * notification = (gatt_client_notification_t*) btstack_linked_list_iterator_next(&it);
if ((notification->con_handle != GATT_CLIENT_ANY_CONNECTION) && (notification->con_handle != con_handle)) continue;
if ((notification->attribute_handle != GATT_CLIENT_ANY_VALUE_HANDLE) && (notification->attribute_handle != attribute_handle)) continue;
(*notification->callback)(HCI_EVENT_PACKET, 0, packet, size);
}
}
static void emit_gatt_complete_event(gatt_client_t * gatt_client, uint8_t att_status){
// @format H1
uint8_t packet[5];
packet[0] = GATT_EVENT_QUERY_COMPLETE;
packet[1] = 3;
little_endian_store_16(packet, 2, gatt_client->con_handle);
packet[4] = att_status;
emit_event_new(gatt_client->callback, packet, sizeof(packet));
}
static void emit_gatt_service_query_result_event(gatt_client_t * gatt_client, uint16_t start_group_handle, uint16_t end_group_handle, const uint8_t * uuid128){
// @format HX
uint8_t packet[24];
packet[0] = GATT_EVENT_SERVICE_QUERY_RESULT;
packet[1] = sizeof(packet) - 2u;
little_endian_store_16(packet, 2, gatt_client->con_handle);
///
little_endian_store_16(packet, 4, start_group_handle);
little_endian_store_16(packet, 6, end_group_handle);
reverse_128(uuid128, &packet[8]);
emit_event_new(gatt_client->callback, packet, sizeof(packet));
}
static void emit_gatt_included_service_query_result_event(gatt_client_t * gatt_client, uint16_t include_handle, uint16_t start_group_handle, uint16_t end_group_handle, const uint8_t * uuid128){
// @format HX
uint8_t packet[26];
packet[0] = GATT_EVENT_INCLUDED_SERVICE_QUERY_RESULT;
packet[1] = sizeof(packet) - 2u;
little_endian_store_16(packet, 2, gatt_client->con_handle);
///
little_endian_store_16(packet, 4, include_handle);
//
little_endian_store_16(packet, 6, start_group_handle);
little_endian_store_16(packet, 8, end_group_handle);
reverse_128(uuid128, &packet[10]);
emit_event_new(gatt_client->callback, packet, sizeof(packet));
}
static void emit_gatt_characteristic_query_result_event(gatt_client_t * gatt_client, uint16_t start_handle, uint16_t value_handle, uint16_t end_handle,
uint16_t properties, const uint8_t * uuid128){
// @format HY
uint8_t packet[28];
packet[0] = GATT_EVENT_CHARACTERISTIC_QUERY_RESULT;
packet[1] = sizeof(packet) - 2u;
little_endian_store_16(packet, 2, gatt_client->con_handle);
///
little_endian_store_16(packet, 4, start_handle);
little_endian_store_16(packet, 6, value_handle);
little_endian_store_16(packet, 8, end_handle);
little_endian_store_16(packet, 10, properties);
reverse_128(uuid128, &packet[12]);
emit_event_new(gatt_client->callback, packet, sizeof(packet));
}
static void emit_gatt_all_characteristic_descriptors_result_event(
gatt_client_t * gatt_client, uint16_t descriptor_handle, const uint8_t * uuid128){
// @format HZ
uint8_t packet[22];
packet[0] = GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT;
packet[1] = sizeof(packet) - 2u;
little_endian_store_16(packet, 2, gatt_client->con_handle);
///
little_endian_store_16(packet, 4, descriptor_handle);
reverse_128(uuid128, &packet[6]);
emit_event_new(gatt_client->callback, packet, sizeof(packet));
}
static void emit_gatt_mtu_exchanged_result_event(gatt_client_t * gatt_client, uint16_t new_mtu){
// @format H2
uint8_t packet[6];
packet[0] = GATT_EVENT_MTU;
packet[1] = sizeof(packet) - 2u;
little_endian_store_16(packet, 2, gatt_client->con_handle);
little_endian_store_16(packet, 4, new_mtu);
att_dispatch_client_mtu_exchanged(gatt_client->con_handle, new_mtu);
emit_event_new(gatt_client->callback, packet, sizeof(packet));
}
// helper
static void gatt_client_handle_transaction_complete(gatt_client_t *gatt_client, uint8_t att_status) {
gatt_client->gatt_client_state = P_READY;
gatt_client_timeout_stop(gatt_client);
emit_gatt_complete_event(gatt_client, att_status);
gatt_client_notify_can_send_query(gatt_client);
}
// @return packet pointer
// @note assume that value is part of an l2cap buffer - overwrite HCI + L2CAP packet headers
#define CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE 8
static uint8_t * setup_characteristic_value_packet(uint8_t type, hci_con_handle_t con_handle, uint16_t attribute_handle, uint8_t * value, uint16_t length){
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// copy value into test packet for testing
static uint8_t packet[1000];
memcpy(&packet[8], value, length);
#else
// before the value inside the ATT PDU
uint8_t * packet = value - CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE;
#endif
packet[0] = type;
packet[1] = CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE - 2 + length;
little_endian_store_16(packet, 2, con_handle);
little_endian_store_16(packet, 4, attribute_handle);
little_endian_store_16(packet, 6, length);
return packet;
}
// @return packet pointer
// @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
#define LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE 10
static uint8_t * setup_long_characteristic_value_packet(uint8_t type, hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * value, uint16_t length){
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// avoid using pre ATT headers.
return NULL;
#endif
#if defined(HCI_INCOMING_PRE_BUFFER_SIZE) && (HCI_INCOMING_PRE_BUFFER_SIZE >= LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE - 8) // L2CAP Header (4) - ACL Header (4)
// before the value inside the ATT PDU
uint8_t * packet = value - LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE;
packet[0] = type;
packet[1] = LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE - 2 + length;
little_endian_store_16(packet, 2, con_handle);
little_endian_store_16(packet, 4, attribute_handle);
little_endian_store_16(packet, 6, offset);
little_endian_store_16(packet, 8, length);
return packet;
#else
log_error("HCI_INCOMING_PRE_BUFFER_SIZE >= 2 required for long characteristic reads");
return NULL;
#endif
}
#if (LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE > CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE)
#define REPORT_PREBUFFER_HEADER LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE
#else
#define REPORT_PREBUFFER_HEADER CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE
#endif
///
static void report_gatt_services(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size){
if (size < 2) return;
uint8_t attr_length = packet[1];
uint8_t uuid_length = attr_length - 4u;
int i;
for (i = 2; (i+attr_length) <= size; i += attr_length){
uint16_t start_group_handle = little_endian_read_16(packet,i);
uint16_t end_group_handle = little_endian_read_16(packet,i+2);
uint8_t uuid128[16];
uint16_t uuid16 = 0;
if (uuid_length == 2u){
uuid16 = little_endian_read_16(packet, i+4);
uuid_add_bluetooth_prefix((uint8_t*) &uuid128, uuid16);
} else if (uuid_length == 16u) {
reverse_128(&packet[i+4], uuid128);
} else {
return;
}
emit_gatt_service_query_result_event(gatt_client, start_group_handle, end_group_handle, uuid128);
}
}
static void report_gatt_characteristic_start_found(gatt_client_t * gatt_client, uint16_t start_handle, uint8_t properties, uint16_t value_handle, uint8_t * uuid, uint16_t uuid_length){
uint8_t uuid128[16];
uint16_t uuid16 = 0;
if (uuid_length == 2u){
uuid16 = little_endian_read_16(uuid, 0);
uuid_add_bluetooth_prefix((uint8_t*) uuid128, uuid16);
} else if (uuid_length == 16u){
reverse_128(uuid, uuid128);
} else {
return;
}
if (gatt_client->filter_with_uuid && (memcmp(gatt_client->uuid128, uuid128, 16) != 0)) return;
gatt_client->characteristic_properties = properties;
gatt_client->characteristic_start_handle = start_handle;
gatt_client->attribute_handle = value_handle;
if (gatt_client->filter_with_uuid) return;
gatt_client->uuid16 = uuid16;
(void)memcpy(gatt_client->uuid128, uuid128, 16);
}
static void report_gatt_characteristic_end_found(gatt_client_t * gatt_client, uint16_t end_handle){
// TODO: stop searching if filter and uuid found
if (!gatt_client->characteristic_start_handle) return;
emit_gatt_characteristic_query_result_event(gatt_client, gatt_client->characteristic_start_handle, gatt_client->attribute_handle,
end_handle, gatt_client->characteristic_properties, gatt_client->uuid128);
gatt_client->characteristic_start_handle = 0;
}
static void report_gatt_characteristics(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size){
if (size < 2u) return;
uint8_t attr_length = packet[1];
if ((attr_length != 7u) && (attr_length != 21u)) return;
uint8_t uuid_length = attr_length - 5u;
int i;
for (i = 2u; (i + attr_length) <= size; i += attr_length){
uint16_t start_handle = little_endian_read_16(packet, i);
uint8_t properties = packet[i+2];
uint16_t value_handle = little_endian_read_16(packet, i+3);
report_gatt_characteristic_end_found(gatt_client, start_handle - 1u);
report_gatt_characteristic_start_found(gatt_client, start_handle, properties, value_handle, &packet[i + 5],
uuid_length);
}
}
static void report_gatt_included_service_uuid16(gatt_client_t * gatt_client, uint16_t include_handle, uint16_t uuid16){
uint8_t normalized_uuid128[16];
uuid_add_bluetooth_prefix(normalized_uuid128, uuid16);
emit_gatt_included_service_query_result_event(gatt_client, include_handle, gatt_client->query_start_handle,
gatt_client->query_end_handle, normalized_uuid128);
}
static void report_gatt_included_service_uuid128(gatt_client_t * gatt_client, uint16_t include_handle, const uint8_t * uuid128){
emit_gatt_included_service_query_result_event(gatt_client, include_handle, gatt_client->query_start_handle,
gatt_client->query_end_handle, uuid128);
}
// @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
static void report_gatt_notification(gatt_client_t *gatt_client, uint16_t value_handle, uint8_t *value, int length) {
if (!gatt_client_accept_server_message(gatt_client)) return;
uint8_t * packet = setup_characteristic_value_packet(GATT_EVENT_NOTIFICATION, gatt_client->con_handle, value_handle, value, length);
emit_event_to_registered_listeners(gatt_client->con_handle, value_handle, packet, CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE + length);
}
// @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
static void report_gatt_indication(gatt_client_t *gatt_client, uint16_t value_handle, uint8_t *value, int length) {
if (!gatt_client_accept_server_message(gatt_client)) return;
uint8_t * packet = setup_characteristic_value_packet(GATT_EVENT_INDICATION, gatt_client->con_handle, value_handle, value, length);
emit_event_to_registered_listeners(gatt_client->con_handle, value_handle, packet, CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE + length);
}
// @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
static void report_gatt_characteristic_value(gatt_client_t * gatt_client, uint16_t attribute_handle, uint8_t * value, uint16_t length){
uint8_t * packet = setup_characteristic_value_packet(GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT, gatt_client->con_handle, attribute_handle, value, length);
emit_event_new(gatt_client->callback, packet, CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE + length);
}
// @note assume that value is part of an l2cap buffer - overwrite parts of the HCI/L2CAP/ATT packet (4/4/3) bytes
static void report_gatt_long_characteristic_value_blob(gatt_client_t * gatt_client, uint16_t attribute_handle, uint8_t * blob, uint16_t blob_length, int value_offset){
uint8_t * packet = setup_long_characteristic_value_packet(GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT, gatt_client->con_handle, attribute_handle, value_offset, blob, blob_length);
if (!packet) return;
emit_event_new(gatt_client->callback, packet, blob_length + LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE);
}
static void report_gatt_characteristic_descriptor(gatt_client_t * gatt_client, uint16_t descriptor_handle, uint8_t *value, uint16_t value_length, uint16_t value_offset){
UNUSED(value_offset);
uint8_t * packet = setup_characteristic_value_packet(GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT, gatt_client->con_handle, descriptor_handle, value, value_length);
emit_event_new(gatt_client->callback, packet, value_length + 8u);
}
static void report_gatt_long_characteristic_descriptor(gatt_client_t * gatt_client, uint16_t descriptor_handle, uint8_t *blob, uint16_t blob_length, uint16_t value_offset){
uint8_t * packet = setup_long_characteristic_value_packet(GATT_EVENT_LONG_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT, gatt_client->con_handle, descriptor_handle, value_offset, blob, blob_length);
if (!packet) return;
emit_event_new(gatt_client->callback, packet, blob_length + LONG_CHARACTERISTIC_VALUE_EVENT_HEADER_SIZE);
}
static void report_gatt_all_characteristic_descriptors(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size, uint16_t pair_size){
int i;
for (i = 0u; (i + pair_size) <= size; i += pair_size){
uint16_t descriptor_handle = little_endian_read_16(packet,i);
uint8_t uuid128[16];
uint16_t uuid16 = 0;
if (pair_size == 4u){
uuid16 = little_endian_read_16(packet,i+2);
uuid_add_bluetooth_prefix(uuid128, uuid16);
} else {
reverse_128(&packet[i+2], uuid128);
}
emit_gatt_all_characteristic_descriptors_result_event(gatt_client, descriptor_handle, uuid128);
}
}
static int is_query_done(gatt_client_t * gatt_client, uint16_t last_result_handle){
return last_result_handle >= gatt_client->end_group_handle;
}
static void trigger_next_query(gatt_client_t * gatt_client, uint16_t last_result_handle, gatt_client_state_t next_query_state){
if (is_query_done(gatt_client, last_result_handle)){
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
return;
}
// next
gatt_client->start_group_handle = last_result_handle + 1u;
gatt_client->gatt_client_state = next_query_state;
}
static void trigger_next_included_service_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_INCLUDED_SERVICE_QUERY);
}
static void trigger_next_service_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_SERVICE_QUERY);
}
static void trigger_next_service_by_uuid_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_SERVICE_WITH_UUID_QUERY);
}
static void trigger_next_characteristic_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
if (is_query_done(gatt_client, last_result_handle)){
// report last characteristic
report_gatt_characteristic_end_found(gatt_client, gatt_client->end_group_handle);
}
trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_ALL_CHARACTERISTICS_OF_SERVICE_QUERY);
}
static void trigger_next_characteristic_descriptor_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY);
}
static void trigger_next_read_by_type_query(gatt_client_t * gatt_client, uint16_t last_result_handle){
trigger_next_query(gatt_client, last_result_handle, P_W2_SEND_READ_BY_TYPE_REQUEST);
}
static void trigger_next_prepare_write_query(gatt_client_t * gatt_client, gatt_client_state_t next_query_state, gatt_client_state_t done_state){
gatt_client->attribute_offset += write_blob_length(gatt_client);
uint16_t next_blob_length = write_blob_length(gatt_client);
if (next_blob_length == 0u){
gatt_client->gatt_client_state = done_state;
return;
}
gatt_client->gatt_client_state = next_query_state;
}
static void trigger_next_blob_query(gatt_client_t * gatt_client, gatt_client_state_t next_query_state, uint16_t received_blob_length){
uint16_t max_blob_length = gatt_client->mtu - 1u;
if (received_blob_length < max_blob_length){
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
return;
}
gatt_client->attribute_offset += received_blob_length;
gatt_client->gatt_client_state = next_query_state;
}
void gatt_client_listen_for_characteristic_value_updates(gatt_client_notification_t * notification, btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
notification->callback = callback;
notification->con_handle = con_handle;
if (characteristic == NULL){
notification->attribute_handle = GATT_CLIENT_ANY_VALUE_HANDLE;
} else {
notification->attribute_handle = characteristic->value_handle;
}
btstack_linked_list_add(&gatt_client_value_listeners, (btstack_linked_item_t*) notification);
}
void gatt_client_stop_listening_for_characteristic_value_updates(gatt_client_notification_t * notification){
btstack_linked_list_remove(&gatt_client_value_listeners, (btstack_linked_item_t*) notification);
}
static int is_value_valid(gatt_client_t *gatt_client, uint8_t *packet, uint16_t size){
uint16_t attribute_handle = little_endian_read_16(packet, 1);
uint16_t value_offset = little_endian_read_16(packet, 3);
if (gatt_client->attribute_handle != attribute_handle) return 0;
if (gatt_client->attribute_offset != value_offset) return 0;
return memcmp(&gatt_client->attribute_value[gatt_client->attribute_offset], &packet[5], size - 5u) == 0u;
}
#ifdef ENABLE_LE_SIGNED_WRITE
static void gatt_client_run_for_client_start_signed_write(gatt_client_t *gatt_client) {
sm_key_t csrk;
le_device_db_local_csrk_get(gatt_client->le_device_index, csrk);
uint32_t sign_counter = le_device_db_local_counter_get(gatt_client->le_device_index);
gatt_client->gatt_client_state = P_W4_CMAC_RESULT;
sm_cmac_signed_write_start(csrk, ATT_SIGNED_WRITE_COMMAND, gatt_client->attribute_handle, gatt_client->attribute_length, gatt_client->attribute_value, sign_counter, att_signed_write_handle_cmac_result);
}
#endif
// returns true if packet was sent
static bool gatt_client_run_for_gatt_client(gatt_client_t * gatt_client){
// wait until re-encryption is complete
if (gap_reconnect_security_setup_active(gatt_client->con_handle)) return false;
// wait until re-encryption is complete
if (gatt_client->reencryption_active) return false;
// wait until pairing complete (either reactive authentication or due to required security level)
if (gatt_client->wait_for_authentication_complete) return false;
bool client_request_pending = gatt_client->gatt_client_state != P_READY;
// verify security level for Mandatory Authentication over LE
bool check_security;
switch (gatt_client->bearer_type){
case ATT_BEARER_UNENHANCED_LE:
check_security = true;
break;
default:
check_security = false;
break;
}
if (client_request_pending && (gatt_client_required_security_level > gatt_client->security_level) && check_security){
log_info("Trigger pairing, current security level %u, required %u\n", gatt_client->security_level, gatt_client_required_security_level);
gatt_client->wait_for_authentication_complete = 1;
// set att error code for pairing failure based on required level
switch (gatt_client_required_security_level){
case LEVEL_4:
case LEVEL_3:
gatt_client->pending_error_code = ATT_ERROR_INSUFFICIENT_AUTHENTICATION;
break;
default:
gatt_client->pending_error_code = ATT_ERROR_INSUFFICIENT_ENCRYPTION;
break;
}
sm_request_pairing(gatt_client->con_handle);
// sm probably just sent a pdu
return true;
}
switch (gatt_client->mtu_state) {
case SEND_MTU_EXCHANGE:
gatt_client->mtu_state = SENT_MTU_EXCHANGE;
att_exchange_mtu_request(gatt_client);
return true;
case SENT_MTU_EXCHANGE:
return false;
default:
break;
}
if (gatt_client->send_confirmation){
gatt_client->send_confirmation = 0;
att_confirmation(gatt_client);
return true;
}
// check MTU for writes
switch (gatt_client->gatt_client_state){
case P_W2_SEND_WRITE_CHARACTERISTIC_VALUE:
case P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR:
if (gatt_client->attribute_length <= (gatt_client->mtu - 3u)) break;
log_error("gatt_client_run: value len %u > MTU %u - 3\n", gatt_client->attribute_length,gatt_client->mtu);
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH);
return false;
default:
break;
}
bool packet_sent = true;
bool done = true;
switch (gatt_client->gatt_client_state){
case P_W2_SEND_SERVICE_QUERY:
gatt_client->gatt_client_state = P_W4_SERVICE_QUERY_RESULT;
send_gatt_services_request(gatt_client);
break;
case P_W2_SEND_SERVICE_WITH_UUID_QUERY:
gatt_client->gatt_client_state = P_W4_SERVICE_WITH_UUID_RESULT;
send_gatt_services_by_uuid_request(gatt_client);
break;
case P_W2_SEND_ALL_CHARACTERISTICS_OF_SERVICE_QUERY:
gatt_client->gatt_client_state = P_W4_ALL_CHARACTERISTICS_OF_SERVICE_QUERY_RESULT;
send_gatt_characteristic_request(gatt_client);
break;
case P_W2_SEND_CHARACTERISTIC_WITH_UUID_QUERY:
gatt_client->gatt_client_state = P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT;
send_gatt_characteristic_request(gatt_client);
break;
case P_W2_SEND_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY:
gatt_client->gatt_client_state = P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT;
send_gatt_characteristic_descriptor_request(gatt_client);
break;
case P_W2_SEND_INCLUDED_SERVICE_QUERY:
gatt_client->gatt_client_state = P_W4_INCLUDED_SERVICE_QUERY_RESULT;
send_gatt_included_service_request(gatt_client);
break;
case P_W2_SEND_INCLUDED_SERVICE_WITH_UUID_QUERY:
gatt_client->gatt_client_state = P_W4_INCLUDED_SERVICE_UUID_WITH_QUERY_RESULT;
send_gatt_included_service_uuid_request(gatt_client);
break;
case P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY:
gatt_client->gatt_client_state = P_W4_READ_CHARACTERISTIC_VALUE_RESULT;
send_gatt_read_characteristic_value_request(gatt_client);
break;
case P_W2_SEND_READ_BLOB_QUERY:
gatt_client->gatt_client_state = P_W4_READ_BLOB_RESULT;
send_gatt_read_blob_request(gatt_client);
break;
case P_W2_SEND_READ_BY_TYPE_REQUEST:
gatt_client->gatt_client_state = P_W4_READ_BY_TYPE_RESPONSE;
send_gatt_read_by_type_request(gatt_client);
break;
case P_W2_SEND_READ_MULTIPLE_REQUEST:
gatt_client->gatt_client_state = P_W4_READ_MULTIPLE_RESPONSE;
send_gatt_read_multiple_request(gatt_client);
break;
#ifdef ENABLE_GATT_OVER_EATT
case P_W2_SEND_READ_MULTIPLE_VARIABLE_REQUEST:
gatt_client->gatt_client_state = P_W4_READ_MULTIPLE_VARIABLE_RESPONSE;
send_gatt_read_multiple_variable_request(gatt_client);
break;
#endif
case P_W2_SEND_WRITE_CHARACTERISTIC_VALUE:
gatt_client->gatt_client_state = P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT;
send_gatt_write_attribute_value_request(gatt_client);
break;
case P_W2_PREPARE_WRITE:
gatt_client->gatt_client_state = P_W4_PREPARE_WRITE_RESULT;
send_gatt_prepare_write_request(gatt_client);
break;
case P_W2_PREPARE_WRITE_SINGLE:
gatt_client->gatt_client_state = P_W4_PREPARE_WRITE_SINGLE_RESULT;
send_gatt_prepare_write_request(gatt_client);
break;
case P_W2_PREPARE_RELIABLE_WRITE:
gatt_client->gatt_client_state = P_W4_PREPARE_RELIABLE_WRITE_RESULT;
send_gatt_prepare_write_request(gatt_client);
break;
case P_W2_EXECUTE_PREPARED_WRITE:
gatt_client->gatt_client_state = P_W4_EXECUTE_PREPARED_WRITE_RESULT;
send_gatt_execute_write_request(gatt_client);
break;
case P_W2_CANCEL_PREPARED_WRITE:
gatt_client->gatt_client_state = P_W4_CANCEL_PREPARED_WRITE_RESULT;
send_gatt_cancel_prepared_write_request(gatt_client);
break;
case P_W2_CANCEL_PREPARED_WRITE_DATA_MISMATCH:
gatt_client->gatt_client_state = P_W4_CANCEL_PREPARED_WRITE_DATA_MISMATCH_RESULT;
send_gatt_cancel_prepared_write_request(gatt_client);
break;
#ifdef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
case P_W2_SEND_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY:
// use Find Information
gatt_client->gatt_client_state = P_W4_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT;
send_gatt_characteristic_descriptor_request(gatt_client);
#else
case P_W2_SEND_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY:
// Use Read By Type
gatt_client->gatt_client_state = P_W4_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT;
send_gatt_read_client_characteristic_configuration_request(gatt_client);
#endif
break;
case P_W2_SEND_READ_CHARACTERISTIC_DESCRIPTOR_QUERY:
gatt_client->gatt_client_state = P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT;
send_gatt_read_characteristic_descriptor_request(gatt_client);
break;
case P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY:
gatt_client->gatt_client_state = P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT;
send_gatt_read_blob_request(gatt_client);
break;
case P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR:
gatt_client->gatt_client_state = P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT;
send_gatt_write_attribute_value_request(gatt_client);
break;
case P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION:
gatt_client->gatt_client_state = P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT;
send_gatt_write_client_characteristic_configuration_request(gatt_client);
break;
case P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR:
gatt_client->gatt_client_state = P_W4_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT;
send_gatt_prepare_write_request(gatt_client);
break;
case P_W2_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR:
gatt_client->gatt_client_state = P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT;
send_gatt_execute_write_request(gatt_client);
break;
#ifdef ENABLE_LE_SIGNED_WRITE
case P_W4_IDENTITY_RESOLVING:
log_info("P_W4_IDENTITY_RESOLVING - state %x", sm_identity_resolving_state(gatt_client->con_handle));
switch (sm_identity_resolving_state(gatt_client->con_handle)){
case IRK_LOOKUP_SUCCEEDED:
gatt_client->le_device_index = sm_le_device_index(gatt_client->con_handle);
gatt_client->gatt_client_state = P_W4_CMAC_READY;
if (sm_cmac_ready()){
gatt_client_run_for_client_start_signed_write(gatt_client);
}
break;
case IRK_LOOKUP_FAILED:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_BONDING_INFORMATION_MISSING);
break;
default:
break;
}
packet_sent = false;
break;
case P_W4_CMAC_READY:
if (sm_cmac_ready()){
gatt_client_run_for_client_start_signed_write(gatt_client);
}
packet_sent = false;
break;
case P_W2_SEND_SIGNED_WRITE: {
gatt_client->gatt_client_state = P_W4_SEND_SINGED_WRITE_DONE;
// bump local signing counter
uint32_t sign_counter = le_device_db_local_counter_get(gatt_client->le_device_index);
le_device_db_local_counter_set(gatt_client->le_device_index, sign_counter + 1);
// send signed write command
send_gatt_signed_write_request(gatt_client, sign_counter);
// finally, notifiy client that write is complete
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
}
#endif
default:
done = false;
break;
}
if (done){
return packet_sent;
}
// write without response callback
btstack_context_callback_registration_t * callback =
(btstack_context_callback_registration_t *) btstack_linked_list_pop(&gatt_client->write_without_response_requests);
if (callback != NULL){
(*callback->callback)(callback->context);
return true;
}
// requested can send now old
if (gatt_client->write_without_response_callback){
btstack_packet_handler_t packet_handler = gatt_client->write_without_response_callback;
gatt_client->write_without_response_callback = NULL;
uint8_t event[4];
event[0] = GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE;
event[1] = sizeof(event) - 2u;
little_endian_store_16(event, 2, gatt_client->con_handle);
packet_handler(HCI_EVENT_PACKET, gatt_client->con_handle, event, sizeof(event));
return true; // to trigger requeueing (even if higher layer didn't sent)
}
return false;
}
static void gatt_client_run(void){
btstack_linked_item_t *it;
bool packet_sent;
#ifdef ENABLE_GATT_OVER_EATT
btstack_linked_list_iterator_t it_eatt;
#endif
for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
gatt_client_t * gatt_client = (gatt_client_t *) it;
switch (gatt_client->bearer_type){
case ATT_BEARER_UNENHANCED_LE:
#ifdef ENABLE_GATT_OVER_EATT
btstack_linked_list_iterator_init(&it_eatt, &gatt_client->eatt_clients);
while (btstack_linked_list_iterator_has_next(&it_eatt)) {
gatt_client_t * eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it_eatt);
if (eatt_client->gatt_client_state != P_READY){
if (l2cap_can_send_packet_now(eatt_client->l2cap_cid)){
gatt_client_run_for_gatt_client(eatt_client);
}
}
}
#endif
if (!att_dispatch_client_can_send_now(gatt_client->con_handle)) {
att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
return;
}
packet_sent = gatt_client_run_for_gatt_client(gatt_client);
if (packet_sent){
// request new permission
att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
// requeue client for fairness and exit
// note: iterator has become invalid
btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
btstack_linked_list_add_tail(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
return;
}
break;
#ifdef ENABLE_GATT_OVER_CLASSIC
case ATT_BEARER_UNENHANCED_CLASSIC:
if (gatt_client->con_handle == HCI_CON_HANDLE_INVALID) {
continue;
}
// handle GATT over BR/EDR
if (l2cap_can_send_packet_now(gatt_client->l2cap_cid) == false){
l2cap_request_can_send_now_event(gatt_client->l2cap_cid);
return;
}
packet_sent = gatt_client_run_for_gatt_client(gatt_client);
if (packet_sent){
// request new permission
att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
// requeue client for fairness and exit
// note: iterator has become invalid
btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
btstack_linked_list_add_tail(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
return;
}
break;
#endif
default:
btstack_unreachable();
break;
}
}
}
static void gatt_client_report_error_if_pending(gatt_client_t *gatt_client, uint8_t att_error_code) {
if (is_ready(gatt_client) == 1) return;
gatt_client_handle_transaction_complete(gatt_client, att_error_code);
}
static void gatt_client_handle_reencryption_complete(const uint8_t * packet){
hci_con_handle_t con_handle = sm_event_reencryption_complete_get_handle(packet);
gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
if (gatt_client == NULL) return;
// update security level
gatt_client->security_level = gatt_client_le_security_level_for_connection(con_handle);
gatt_client->reencryption_result = sm_event_reencryption_complete_get_status(packet);
gatt_client->reencryption_active = false;
gatt_client->wait_for_authentication_complete = 0;
if (gatt_client->gatt_client_state == P_READY) return;
switch (sm_event_reencryption_complete_get_status(packet)){
case ERROR_CODE_SUCCESS:
log_info("re-encryption success, retry operation");
break;
case ERROR_CODE_AUTHENTICATION_FAILURE:
case ERROR_CODE_PIN_OR_KEY_MISSING:
#if defined(ENABLE_GATT_CLIENT_PAIRING) && !defined(ENABLE_LE_PROACTIVE_AUTHENTICATION)
if (gatt_client_required_security_level == LEVEL_0) {
// re-encryption failed for reactive authentication with pairing and we have a pending client request
// => try to resolve it by deleting bonding information if we started pairing before
// delete bonding information
int le_device_db_index = sm_le_device_index(gatt_client->con_handle);
btstack_assert(le_device_db_index >= 0);
log_info("reactive auth with pairing: delete bonding and start pairing");
#ifdef ENABLE_LE_PRIVACY_ADDRESS_RESOLUTION
hci_remove_le_device_db_entry_from_resolving_list((uint16_t) le_device_db_index);
#endif
le_device_db_remove(le_device_db_index);
// trigger pairing again
sm_request_pairing(gatt_client->con_handle);
break;
}
#endif
// report bonding information missing
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_BONDING_INFORMATION_MISSING);
break;
default:
// report bonding information missing
gatt_client_handle_transaction_complete(gatt_client, gatt_client->pending_error_code);
break;
}
}
static void gatt_client_handle_disconnection_complete(const uint8_t * packet){
log_info("GATT Client: HCI_EVENT_DISCONNECTION_COMPLETE");
hci_con_handle_t con_handle = little_endian_read_16(packet,3);
gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
if (gatt_client == NULL) return;
gatt_client_report_error_if_pending(gatt_client, ATT_ERROR_HCI_DISCONNECT_RECEIVED);
gatt_client_timeout_stop(gatt_client);
btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
btstack_memory_gatt_client_free(gatt_client);
}
static void gatt_client_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel); // ok: handling own l2cap events
UNUSED(size); // ok: there is no channel
if (packet_type != HCI_EVENT_PACKET) return;
hci_con_handle_t con_handle;
gatt_client_t * gatt_client;
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_DISCONNECTION_COMPLETE:
gatt_client_handle_disconnection_complete(packet);
break;
// Pairing complete (with/without bonding=storing of pairing information)
case SM_EVENT_PAIRING_COMPLETE:
con_handle = sm_event_pairing_complete_get_handle(packet);
gatt_client = gatt_client_get_context_for_handle(con_handle);
if (gatt_client == NULL) break;
// update security level
gatt_client->security_level = gatt_client_le_security_level_for_connection(con_handle);
if (gatt_client->wait_for_authentication_complete){
gatt_client->wait_for_authentication_complete = 0;
if (sm_event_pairing_complete_get_status(packet)){
log_info("pairing failed, report previous error 0x%x", gatt_client->pending_error_code);
gatt_client_report_error_if_pending(gatt_client, gatt_client->pending_error_code);
} else {
log_info("pairing success, retry operation");
}
}
break;
#ifdef ENABLE_LE_SIGNED_WRITE
// Identity Resolving completed (no code, gatt_client_run will continue)
case SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED:
case SM_EVENT_IDENTITY_RESOLVING_FAILED:
break;
#endif
// re-encryption started
case SM_EVENT_REENCRYPTION_STARTED:
con_handle = sm_event_reencryption_complete_get_handle(packet);
gatt_client = gatt_client_get_context_for_handle(con_handle);
if (gatt_client == NULL) break;
gatt_client->reencryption_active = true;
gatt_client->reencryption_result = ERROR_CODE_SUCCESS;
break;
// re-encryption complete
case SM_EVENT_REENCRYPTION_COMPLETE:
gatt_client_handle_reencryption_complete(packet);
break;
default:
break;
}
gatt_client_run();
}
static void gatt_client_handle_att_read_response(gatt_client_t *gatt_client, uint8_t *packet, uint16_t size) {
switch (gatt_client->gatt_client_state) {
case P_W4_INCLUDED_SERVICE_UUID_WITH_QUERY_RESULT:
if (size >= 17) {
uint8_t uuid128[16];
reverse_128(&packet[1], uuid128);
report_gatt_included_service_uuid128(gatt_client, gatt_client->start_group_handle, uuid128);
}
trigger_next_included_service_query(gatt_client, gatt_client->start_group_handle);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
case P_W4_READ_CHARACTERISTIC_VALUE_RESULT:
report_gatt_characteristic_value(gatt_client, gatt_client->attribute_handle, &packet[1], size - 1u);
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
case P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT:
report_gatt_characteristic_descriptor(gatt_client, gatt_client->attribute_handle, &packet[1],
size - 1u, 0u);
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
// Use ATT_READ_REQUEST for first blob of Read Long Characteristic
case P_W4_READ_BLOB_RESULT:
report_gatt_long_characteristic_value_blob(gatt_client, gatt_client->attribute_handle, &packet[1],
size - 1u, gatt_client->attribute_offset);
trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_QUERY, size - 1u);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
// Use ATT_READ_REQUEST for first blob of Read Long Characteristic Descriptor
case P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT:
report_gatt_long_characteristic_descriptor(gatt_client, gatt_client->attribute_handle, &packet[1],
size - 1u, gatt_client->attribute_offset);
trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY,
size - 1u);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
default:
break;
}
}
static void gatt_client_handle_att_read_by_type_response(gatt_client_t *gatt_client, uint8_t *packet, uint16_t size) {
switch (gatt_client->gatt_client_state) {
case P_W4_ALL_CHARACTERISTICS_OF_SERVICE_QUERY_RESULT:
report_gatt_characteristics(gatt_client, packet, size);
trigger_next_characteristic_query(gatt_client,
get_last_result_handle_from_characteristics_list(packet, size));
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done, or by ATT_ERROR
break;
case P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT:
report_gatt_characteristics(gatt_client, packet, size);
trigger_next_characteristic_query(gatt_client,
get_last_result_handle_from_characteristics_list(packet, size));
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done, or by ATT_ERROR
break;
case P_W4_INCLUDED_SERVICE_QUERY_RESULT: {
if (size < 2u) break;
uint16_t uuid16 = 0;
uint16_t pair_size = packet[1];
if (pair_size == 6u) {
if (size < 8u) break;
// UUIDs not available, query first included service
gatt_client->start_group_handle = little_endian_read_16(packet, 2); // ready for next query
gatt_client->query_start_handle = little_endian_read_16(packet, 4);
gatt_client->query_end_handle = little_endian_read_16(packet, 6);
gatt_client->gatt_client_state = P_W2_SEND_INCLUDED_SERVICE_WITH_UUID_QUERY;
break;
}
if (pair_size != 8u) break;
// UUIDs included, report all of them
uint16_t offset;
for (offset = 2u; (offset + 8u) <= size; offset += pair_size) {
uint16_t include_handle = little_endian_read_16(packet, offset);
gatt_client->query_start_handle = little_endian_read_16(packet, offset + 2u);
gatt_client->query_end_handle = little_endian_read_16(packet, offset + 4u);
uuid16 = little_endian_read_16(packet, offset + 6u);
report_gatt_included_service_uuid16(gatt_client, include_handle, uuid16);
}
trigger_next_included_service_query(gatt_client,
get_last_result_handle_from_included_services_list(packet,
size));
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
}
#ifndef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
case P_W4_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT:
gatt_client->client_characteristic_configuration_handle = little_endian_read_16(packet, 2);
gatt_client->gatt_client_state = P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION;
break;
#endif
case P_W4_READ_BY_TYPE_RESPONSE: {
uint16_t pair_size = packet[1];
// set last result handle to last valid handle, only used if pair_size invalid
uint16_t last_result_handle = 0xffff;
if (pair_size > 2) {
uint16_t offset;
for (offset = 2; offset < size; offset += pair_size) {
uint16_t value_handle = little_endian_read_16(packet, offset);
report_gatt_characteristic_value(gatt_client, value_handle, &packet[offset + 2u],
pair_size - 2u);
last_result_handle = value_handle;
}
}
trigger_next_read_by_type_query(gatt_client, last_result_handle);
break;
}
default:
break;
}
}
static void gatt_client_handle_att_write_response(gatt_client_t *gatt_client) {
switch (gatt_client->gatt_client_state) {
case P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
case P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
case P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
default:
break;
}
}
static void gatt_client_handle_att_response(gatt_client_t * gatt_client, uint8_t * packet, uint16_t size) {
uint8_t att_status;
switch (packet[0]) {
case ATT_EXCHANGE_MTU_RESPONSE: {
if (size < 3u) break;
bool update_gatt_server_att_mtu = false;
uint16_t remote_rx_mtu = little_endian_read_16(packet, 1);
uint16_t local_rx_mtu = l2cap_max_le_mtu();
switch (gatt_client->bearer_type){
case ATT_BEARER_UNENHANCED_LE:
update_gatt_server_att_mtu = true;
break;
#ifdef ENABLE_GATT_OVER_CLASSIC
case ATT_BEARER_UNENHANCED_CLASSIC:
local_rx_mtu = gatt_client->mtu;
break;
#endif
default:
btstack_unreachable();
break;
}
uint16_t mtu = (remote_rx_mtu < local_rx_mtu) ? remote_rx_mtu : local_rx_mtu;
// set gatt client mtu
gatt_client->mtu = mtu;
gatt_client->mtu_state = MTU_EXCHANGED;
if (update_gatt_server_att_mtu){
// set per connection mtu state - for fixed channel
hci_connection_t *hci_connection = hci_connection_for_handle(gatt_client->con_handle);
hci_connection->att_connection.mtu = gatt_client->mtu;
hci_connection->att_connection.mtu_exchanged = true;
}
emit_gatt_mtu_exchanged_result_event(gatt_client, gatt_client->mtu);
break;
}
case ATT_READ_BY_GROUP_TYPE_RESPONSE:
switch (gatt_client->gatt_client_state) {
case P_W4_SERVICE_QUERY_RESULT:
report_gatt_services(gatt_client, packet, size);
trigger_next_service_query(gatt_client, get_last_result_handle_from_service_list(packet, size));
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
default:
break;
}
break;
case ATT_HANDLE_VALUE_NOTIFICATION:
if (size < 3u) return;
report_gatt_notification(gatt_client, little_endian_read_16(packet, 1u), &packet[3], size - 3u);
return;
#ifdef ENABLE_GATT_OVER_EATT
case ATT_MULTIPLE_HANDLE_VALUE_NTF:
if (size >= 5u) {
uint16_t offset = 1;
while (true){
uint16_t value_handle = little_endian_read_16(packet, offset);
offset += 2;
uint16_t value_length = little_endian_read_16(packet, offset);
offset += 2;
if ((offset + value_length) > size) break;
report_gatt_notification(gatt_client, value_handle, &packet[offset], value_length);
offset += value_length;
}
}
return;
#endif
case ATT_HANDLE_VALUE_INDICATION:
if (size < 3u) break;
report_gatt_indication(gatt_client, little_endian_read_16(packet, 1u), &packet[3], size - 3u);
gatt_client->send_confirmation = 1;
break;
case ATT_READ_BY_TYPE_RESPONSE:
gatt_client_handle_att_read_by_type_response(gatt_client, packet, size);
break;
case ATT_READ_RESPONSE:
gatt_client_handle_att_read_response(gatt_client, packet, size);
break;
case ATT_FIND_BY_TYPE_VALUE_RESPONSE: {
uint8_t pair_size = 4;
int i;
uint16_t start_group_handle;
uint16_t end_group_handle = 0xffff; // asserts GATT_EVENT_QUERY_COMPLETE is emitted if no results
for (i = 1u; (i + pair_size) <= size; i += pair_size) {
start_group_handle = little_endian_read_16(packet, i);
end_group_handle = little_endian_read_16(packet, i + 2);
emit_gatt_service_query_result_event(gatt_client, start_group_handle, end_group_handle,
gatt_client->uuid128);
}
trigger_next_service_by_uuid_query(gatt_client, end_group_handle);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
}
case ATT_FIND_INFORMATION_REPLY: {
if (size < 2u) break;
uint8_t pair_size = 4;
if (packet[1u] == 2u) {
pair_size = 18;
}
uint16_t offset = 2;
if (size < (pair_size + offset)) break;
uint16_t last_descriptor_handle = little_endian_read_16(packet, size - pair_size);
#ifdef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
log_info("ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY, state %x", gatt_client->gatt_client_state);
if (gatt_client->gatt_client_state == P_W4_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY_RESULT){
// iterate over descriptors looking for CCC
if (pair_size == 4){
while ((offset + 4) <= size){
uint16_t uuid16 = little_endian_read_16(packet, offset + 2);
if (uuid16 == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION){
gatt_client->client_characteristic_configuration_handle = little_endian_read_16(packet, offset);
gatt_client->gatt_client_state = P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION;
log_info("CCC found %x", gatt_client->client_characteristic_configuration_handle);
break;
}
offset += pair_size;
}
}
if (is_query_done(gatt_client, last_descriptor_handle)){
} else {
// next
gatt_client->start_group_handle = last_descriptor_handle + 1;
gatt_client->gatt_client_state = P_W2_SEND_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY;
}
break;
}
#endif
report_gatt_all_characteristic_descriptors(gatt_client, &packet[2], size - 2u, pair_size);
trigger_next_characteristic_descriptor_query(gatt_client, last_descriptor_handle);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
}
case ATT_WRITE_RESPONSE:
gatt_client_handle_att_write_response(gatt_client);
break;
case ATT_READ_BLOB_RESPONSE: {
uint16_t received_blob_length = size - 1u;
switch (gatt_client->gatt_client_state) {
case P_W4_READ_BLOB_RESULT:
report_gatt_long_characteristic_value_blob(gatt_client, gatt_client->attribute_handle, &packet[1],
received_blob_length, gatt_client->attribute_offset);
trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_QUERY, received_blob_length);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
case P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT:
report_gatt_long_characteristic_descriptor(gatt_client, gatt_client->attribute_handle,
&packet[1], received_blob_length,
gatt_client->attribute_offset);
trigger_next_blob_query(gatt_client, P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY,
received_blob_length);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
default:
break;
}
break;
}
case ATT_PREPARE_WRITE_RESPONSE:
switch (gatt_client->gatt_client_state) {
case P_W4_PREPARE_WRITE_SINGLE_RESULT:
if (is_value_valid(gatt_client, packet, size)) {
att_status = ATT_ERROR_SUCCESS;
} else {
att_status = ATT_ERROR_DATA_MISMATCH;
}
gatt_client_handle_transaction_complete(gatt_client, att_status);
break;
case P_W4_PREPARE_WRITE_RESULT: {
gatt_client->attribute_offset = little_endian_read_16(packet, 3);
trigger_next_prepare_write_query(gatt_client, P_W2_PREPARE_WRITE, P_W2_EXECUTE_PREPARED_WRITE);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
}
case P_W4_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT: {
gatt_client->attribute_offset = little_endian_read_16(packet, 3);
trigger_next_prepare_write_query(gatt_client, P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR,
P_W2_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
}
case P_W4_PREPARE_RELIABLE_WRITE_RESULT: {
if (is_value_valid(gatt_client, packet, size)) {
gatt_client->attribute_offset = little_endian_read_16(packet, 3);
trigger_next_prepare_write_query(gatt_client, P_W2_PREPARE_RELIABLE_WRITE,
P_W2_EXECUTE_PREPARED_WRITE);
// GATT_EVENT_QUERY_COMPLETE is emitted by trigger_next_xxx when done
break;
}
gatt_client->gatt_client_state = P_W2_CANCEL_PREPARED_WRITE_DATA_MISMATCH;
break;
}
default:
break;
}
break;
case ATT_EXECUTE_WRITE_RESPONSE:
switch (gatt_client->gatt_client_state) {
case P_W4_EXECUTE_PREPARED_WRITE_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
case P_W4_CANCEL_PREPARED_WRITE_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
case P_W4_CANCEL_PREPARED_WRITE_DATA_MISMATCH_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_DATA_MISMATCH);
break;
case P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
default:
break;
}
break;
case ATT_READ_MULTIPLE_RESPONSE:
switch (gatt_client->gatt_client_state) {
case P_W4_READ_MULTIPLE_RESPONSE:
report_gatt_characteristic_value(gatt_client, 0u, &packet[1], size - 1u);
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
default:
break;
}
break;
#ifdef ENABLE_GATT_OVER_EATT
case ATT_READ_MULTIPLE_VARIABLE_RSP:
switch (gatt_client->gatt_client_state) {
case P_W4_READ_MULTIPLE_VARIABLE_RESPONSE:
report_gatt_characteristic_value(gatt_client, 0u, &packet[1], size - 1u);
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
default:
break;
}
break;
#endif
case ATT_ERROR_RESPONSE:
if (size < 5u) return;
att_status = packet[4];
switch (att_status) {
case ATT_ERROR_ATTRIBUTE_NOT_FOUND: {
switch (gatt_client->gatt_client_state) {
case P_W4_SERVICE_QUERY_RESULT:
case P_W4_SERVICE_WITH_UUID_RESULT:
case P_W4_INCLUDED_SERVICE_QUERY_RESULT:
case P_W4_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
case P_W4_ALL_CHARACTERISTICS_OF_SERVICE_QUERY_RESULT:
case P_W4_CHARACTERISTIC_WITH_UUID_QUERY_RESULT:
report_gatt_characteristic_end_found(gatt_client, gatt_client->end_group_handle);
gatt_client_handle_transaction_complete(gatt_client, ATT_ERROR_SUCCESS);
break;
case P_W4_READ_BY_TYPE_RESPONSE:
if (gatt_client->start_group_handle == gatt_client->query_start_handle) {
att_status = ATT_ERROR_ATTRIBUTE_NOT_FOUND;
} else {
att_status = ATT_ERROR_SUCCESS;
}
gatt_client_handle_transaction_complete(gatt_client, att_status);
break;
default:
gatt_client_report_error_if_pending(gatt_client, att_status);
break;
}
break;
}
#ifdef ENABLE_GATT_CLIENT_PAIRING
case ATT_ERROR_INSUFFICIENT_AUTHENTICATION:
case ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
case ATT_ERROR_INSUFFICIENT_ENCRYPTION: {
// security too low
if (gatt_client->security_counter > 0) {
gatt_client_report_error_if_pending(gatt_client, att_status);
break;
}
// start security
gatt_client->security_counter++;
// setup action
int retry = 1;
switch (gatt_client->gatt_client_state){
case P_W4_READ_CHARACTERISTIC_VALUE_RESULT:
gatt_client->gatt_client_state = P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY ;
break;
case P_W4_READ_BLOB_RESULT:
gatt_client->gatt_client_state = P_W2_SEND_READ_BLOB_QUERY;
break;
case P_W4_READ_BY_TYPE_RESPONSE:
gatt_client->gatt_client_state = P_W2_SEND_READ_BY_TYPE_REQUEST;
break;
case P_W4_READ_MULTIPLE_RESPONSE:
gatt_client->gatt_client_state = P_W2_SEND_READ_MULTIPLE_REQUEST;
break;
case P_W4_READ_MULTIPLE_VARIABLE_RESPONSE:
gatt_client->gatt_client_state = P_W2_SEND_READ_MULTIPLE_VARIABLE_REQUEST;
break;
case P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT:
gatt_client->gatt_client_state = P_W2_SEND_WRITE_CHARACTERISTIC_VALUE;
break;
case P_W4_PREPARE_WRITE_RESULT:
gatt_client->gatt_client_state = P_W2_PREPARE_WRITE;
break;
case P_W4_PREPARE_WRITE_SINGLE_RESULT:
gatt_client->gatt_client_state = P_W2_PREPARE_WRITE_SINGLE;
break;
case P_W4_PREPARE_RELIABLE_WRITE_RESULT:
gatt_client->gatt_client_state = P_W2_PREPARE_RELIABLE_WRITE;
break;
case P_W4_EXECUTE_PREPARED_WRITE_RESULT:
gatt_client->gatt_client_state = P_W2_EXECUTE_PREPARED_WRITE;
break;
case P_W4_CANCEL_PREPARED_WRITE_RESULT:
gatt_client->gatt_client_state = P_W2_CANCEL_PREPARED_WRITE;
break;
case P_W4_CANCEL_PREPARED_WRITE_DATA_MISMATCH_RESULT:
gatt_client->gatt_client_state = P_W2_CANCEL_PREPARED_WRITE_DATA_MISMATCH;
break;
case P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT:
gatt_client->gatt_client_state = P_W2_SEND_READ_CHARACTERISTIC_DESCRIPTOR_QUERY;
break;
case P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT:
gatt_client->gatt_client_state = P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY;
break;
case P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
gatt_client->gatt_client_state = P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR;
break;
case P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT:
gatt_client->gatt_client_state = P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION;
break;
case P_W4_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
gatt_client->gatt_client_state = P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR;
break;
case P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT:
gatt_client->gatt_client_state = P_W2_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR;
break;
#ifdef ENABLE_LE_SIGNED_WRITE
case P_W4_SEND_SINGED_WRITE_DONE:
gatt_client->gatt_client_state = P_W2_SEND_SIGNED_WRITE;
break;
#endif
default:
log_info("retry not supported for state %x", gatt_client->gatt_client_state);
retry = 0;
break;
}
if (!retry) {
gatt_client_report_error_if_pending(gatt_client, att_status);
break;
}
log_info("security error, start pairing");
// start pairing for higher security level
gatt_client->wait_for_authentication_complete = 1;
gatt_client->pending_error_code = att_status;
sm_request_pairing(gatt_client->con_handle);
break;
}
#endif
// nothing we can do about that
case ATT_ERROR_INSUFFICIENT_AUTHORIZATION:
default:
gatt_client_report_error_if_pending(gatt_client, att_status);
break;
}
break;
default:
log_info("ATT Handler, unhandled response type 0x%02x", packet[0]);
break;
}
}
static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size) {
gatt_client_t *gatt_client;
if (size < 1u) return;
if (packet_type == HCI_EVENT_PACKET) {
switch (packet[0]) {
case L2CAP_EVENT_CAN_SEND_NOW:
gatt_client_run();
break;
// att_server has negotiated the mtu for this connection, cache if context exists
case ATT_EVENT_MTU_EXCHANGE_COMPLETE:
if (size < 6u) break;
gatt_client = gatt_client_get_context_for_handle(handle);
if (gatt_client == NULL) break;
gatt_client->mtu = little_endian_read_16(packet, 4);
break;
default:
break;
}
return;
}
if (packet_type != ATT_DATA_PACKET) return;
// special cases: notifications & indications motivate creating context
switch (packet[0]) {
case ATT_HANDLE_VALUE_NOTIFICATION:
case ATT_HANDLE_VALUE_INDICATION:
gatt_client_provide_context_for_handle(handle, &gatt_client);
break;
default:
gatt_client = gatt_client_get_context_for_handle(handle);
break;
}
if (gatt_client != NULL) {
gatt_client_handle_att_response(gatt_client, packet, size);
gatt_client_run();
}
}
#ifdef ENABLE_LE_SIGNED_WRITE
static void att_signed_write_handle_cmac_result(uint8_t hash[8]){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &gatt_client_connections);
while (btstack_linked_list_iterator_has_next(&it)){
gatt_client_t * gatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
if (gatt_client->gatt_client_state == P_W4_CMAC_RESULT){
// store result
(void)memcpy(gatt_client->cmac, hash, 8);
// reverse_64(hash, gatt_client->cmac);
gatt_client->gatt_client_state = P_W2_SEND_SIGNED_WRITE;
gatt_client_run();
return;
}
}
}
uint8_t gatt_client_signed_write_without_response(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t message_len, uint8_t * message){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
if (is_ready(gatt_client) == 0){
return GATT_CLIENT_IN_WRONG_STATE;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = value_handle;
gatt_client->attribute_length = message_len;
gatt_client->attribute_value = message;
gatt_client->gatt_client_state = P_W4_IDENTITY_RESOLVING;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
#endif
uint8_t gatt_client_discover_primary_services(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = 0x0001;
gatt_client->end_group_handle = 0xffff;
gatt_client->gatt_client_state = P_W2_SEND_SERVICE_QUERY;
gatt_client->uuid16 = GATT_PRIMARY_SERVICE_UUID;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_discover_secondary_services(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = 0x0001;
gatt_client->end_group_handle = 0xffff;
gatt_client->gatt_client_state = P_W2_SEND_SERVICE_QUERY;
gatt_client->uuid16 = GATT_SECONDARY_SERVICE_UUID;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_discover_primary_services_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t uuid16){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = 0x0001;
gatt_client->end_group_handle = 0xffff;
gatt_client->gatt_client_state = P_W2_SEND_SERVICE_WITH_UUID_QUERY;
gatt_client->uuid16 = uuid16;
uuid_add_bluetooth_prefix((uint8_t*) &(gatt_client->uuid128), gatt_client->uuid16);
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_discover_primary_services_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, const uint8_t * uuid128){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = 0x0001;
gatt_client->end_group_handle = 0xffff;
gatt_client->uuid16 = 0;
(void)memcpy(gatt_client->uuid128, uuid128, 16);
gatt_client->gatt_client_state = P_W2_SEND_SERVICE_WITH_UUID_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_discover_characteristics_for_service(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = service->start_group_handle;
gatt_client->end_group_handle = service->end_group_handle;
gatt_client->filter_with_uuid = 0;
gatt_client->characteristic_start_handle = 0;
gatt_client->gatt_client_state = P_W2_SEND_ALL_CHARACTERISTICS_OF_SERVICE_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_find_included_services_for_service(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = service->start_group_handle;
gatt_client->end_group_handle = service->end_group_handle;
gatt_client->gatt_client_state = P_W2_SEND_INCLUDED_SERVICE_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_discover_characteristics_for_handle_range_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = start_handle;
gatt_client->end_group_handle = end_handle;
gatt_client->filter_with_uuid = 1;
gatt_client->uuid16 = uuid16;
uuid_add_bluetooth_prefix((uint8_t*) &(gatt_client->uuid128), uuid16);
gatt_client->characteristic_start_handle = 0;
gatt_client->gatt_client_state = P_W2_SEND_CHARACTERISTIC_WITH_UUID_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_discover_characteristics_for_handle_range_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = start_handle;
gatt_client->end_group_handle = end_handle;
gatt_client->filter_with_uuid = 1;
gatt_client->uuid16 = 0;
(void)memcpy(gatt_client->uuid128, uuid128, 16);
gatt_client->characteristic_start_handle = 0;
gatt_client->gatt_client_state = P_W2_SEND_CHARACTERISTIC_WITH_UUID_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_discover_characteristics_for_service_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service, uint16_t uuid16){
return gatt_client_discover_characteristics_for_handle_range_by_uuid16(callback, con_handle, service->start_group_handle, service->end_group_handle, uuid16);
}
uint8_t gatt_client_discover_characteristics_for_service_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_service_t * service, const uint8_t * uuid128){
return gatt_client_discover_characteristics_for_handle_range_by_uuid128(callback, con_handle, service->start_group_handle, service->end_group_handle, uuid128);
}
uint8_t gatt_client_discover_characteristic_descriptors(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
if (characteristic->value_handle == characteristic->end_handle){
return ERROR_CODE_SUCCESS;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = characteristic->value_handle + 1u;
gatt_client->end_group_handle = characteristic->end_handle;
gatt_client->gatt_client_state = P_W2_SEND_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = value_handle;
gatt_client->attribute_offset = 0;
gatt_client->gatt_client_state = P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_value_of_characteristics_by_uuid16(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = start_handle;
gatt_client->end_group_handle = end_handle;
gatt_client->query_start_handle = start_handle;
gatt_client->query_end_handle = end_handle;
gatt_client->uuid16 = uuid16;
uuid_add_bluetooth_prefix((uint8_t*) &(gatt_client->uuid128), uuid16);
gatt_client->gatt_client_state = P_W2_SEND_READ_BY_TYPE_REQUEST;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_value_of_characteristics_by_uuid128(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = start_handle;
gatt_client->end_group_handle = end_handle;
gatt_client->query_start_handle = start_handle;
gatt_client->query_end_handle = end_handle;
gatt_client->uuid16 = 0;
(void)memcpy(gatt_client->uuid128, uuid128, 16);
gatt_client->gatt_client_state = P_W2_SEND_READ_BY_TYPE_REQUEST;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
return gatt_client_read_value_of_characteristic_using_value_handle(callback, con_handle, characteristic->value_handle);
}
uint8_t gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t offset){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = value_handle;
gatt_client->attribute_offset = offset;
gatt_client->gatt_client_state = P_W2_SEND_READ_BLOB_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_long_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle){
return gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset(callback, con_handle, value_handle, 0);
}
uint8_t gatt_client_read_long_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic){
return gatt_client_read_long_value_of_characteristic_using_value_handle(callback, con_handle, characteristic->value_handle);
}
static uint8_t gatt_client_read_multiple_characteristic_values_with_state(btstack_packet_handler_t callback, hci_con_handle_t con_handle, int num_value_handles, uint16_t * value_handles, gatt_client_state_t state){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
#ifdef ENABLE_GATT_OVER_EATT
if (state == P_W2_SEND_READ_MULTIPLE_VARIABLE_REQUEST){
if (gatt_client->bearer_type != ATT_BEARER_ENHANCED_LE){
return ERROR_CODE_COMMAND_DISALLOWED;
}
}
#endif
gatt_client->callback = callback;
gatt_client->read_multiple_handle_count = num_value_handles;
gatt_client->read_multiple_handles = value_handles;
gatt_client->gatt_client_state = state;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_multiple_characteristic_values(btstack_packet_handler_t callback, hci_con_handle_t con_handle, int num_value_handles, uint16_t * value_handles){
return gatt_client_read_multiple_characteristic_values_with_state(callback, con_handle, num_value_handles, value_handles, P_W2_SEND_READ_MULTIPLE_REQUEST);
}
#ifdef ENABLE_GATT_OVER_EATT
uint8_t gatt_client_read_multiple_variable_characteristic_values(btstack_packet_handler_t callback, hci_con_handle_t con_handle, int num_value_handles, uint16_t * value_handles){
return gatt_client_read_multiple_characteristic_values_with_state(callback, con_handle, num_value_handles, value_handles, P_W2_SEND_READ_MULTIPLE_VARIABLE_REQUEST);
}
#endif
uint8_t gatt_client_write_value_of_characteristic_without_response(hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
if (value_length > (gatt_client->mtu - 3u)) return GATT_CLIENT_VALUE_TOO_LONG;
if (!att_dispatch_client_can_send_now(gatt_client->con_handle)) return GATT_CLIENT_BUSY;
return att_write_request(gatt_client, ATT_WRITE_COMMAND, value_handle, value_length, value);
}
uint8_t gatt_client_write_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = value_handle;
gatt_client->attribute_length = value_length;
gatt_client->attribute_value = value;
gatt_client->gatt_client_state = P_W2_SEND_WRITE_CHARACTERISTIC_VALUE;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_write_long_value_of_characteristic_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t offset, uint16_t value_length, uint8_t * value){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = value_handle;
gatt_client->attribute_length = value_length;
gatt_client->attribute_offset = offset;
gatt_client->attribute_value = value;
gatt_client->gatt_client_state = P_W2_PREPARE_WRITE;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_write_long_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){
return gatt_client_write_long_value_of_characteristic_with_offset(callback, con_handle, value_handle, 0, value_length, value);
}
uint8_t gatt_client_reliable_write_long_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t * value){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = value_handle;
gatt_client->attribute_length = value_length;
gatt_client->attribute_offset = 0;
gatt_client->attribute_value = value;
gatt_client->gatt_client_state = P_W2_PREPARE_RELIABLE_WRITE;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_write_client_characteristic_configuration(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_t * characteristic, uint16_t configuration){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
if ( (configuration & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) &&
((characteristic->properties & ATT_PROPERTY_NOTIFY) == 0u)) {
log_info("gatt_client_write_client_characteristic_configuration: GATT_CLIENT_CHARACTERISTIC_NOTIFICATION_NOT_SUPPORTED");
return GATT_CLIENT_CHARACTERISTIC_NOTIFICATION_NOT_SUPPORTED;
} else if ( (configuration & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION) &&
((characteristic->properties & ATT_PROPERTY_INDICATE) == 0u)){
log_info("gatt_client_write_client_characteristic_configuration: GATT_CLIENT_CHARACTERISTIC_INDICATION_NOT_SUPPORTED");
return GATT_CLIENT_CHARACTERISTIC_INDICATION_NOT_SUPPORTED;
}
gatt_client->callback = callback;
gatt_client->start_group_handle = characteristic->value_handle;
gatt_client->end_group_handle = characteristic->end_handle;
little_endian_store_16(gatt_client->client_characteristic_configuration_value, 0, configuration);
#ifdef ENABLE_GATT_FIND_INFORMATION_FOR_CCC_DISCOVERY
gatt_client->gatt_client_state = P_W2_SEND_FIND_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY;
#else
gatt_client->gatt_client_state = P_W2_SEND_READ_CLIENT_CHARACTERISTIC_CONFIGURATION_QUERY;
#endif
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = descriptor_handle;
gatt_client->gatt_client_state = P_W2_SEND_READ_CHARACTERISTIC_DESCRIPTOR_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor){
return gatt_client_read_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle);
}
uint8_t gatt_client_read_long_characteristic_descriptor_using_descriptor_handle_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t offset){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = descriptor_handle;
gatt_client->attribute_offset = offset;
gatt_client->gatt_client_state = P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_read_long_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle){
return gatt_client_read_long_characteristic_descriptor_using_descriptor_handle_with_offset(callback, con_handle, descriptor_handle, 0);
}
uint8_t gatt_client_read_long_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor){
return gatt_client_read_long_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle);
}
uint8_t gatt_client_write_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t value_length, uint8_t * value){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = descriptor_handle;
gatt_client->attribute_length = value_length;
gatt_client->attribute_offset = 0;
gatt_client->attribute_value = value;
gatt_client->gatt_client_state = P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_write_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor, uint16_t value_length, uint8_t * value){
return gatt_client_write_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle, value_length, value);
}
uint8_t gatt_client_write_long_characteristic_descriptor_using_descriptor_handle_with_offset(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t offset, uint16_t value_length, uint8_t * value){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = descriptor_handle;
gatt_client->attribute_length = value_length;
gatt_client->attribute_offset = offset;
gatt_client->attribute_value = value;
gatt_client->gatt_client_state = P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_write_long_characteristic_descriptor_using_descriptor_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t descriptor_handle, uint16_t value_length, uint8_t * value){
return gatt_client_write_long_characteristic_descriptor_using_descriptor_handle_with_offset(callback, con_handle, descriptor_handle, 0, value_length, value);
}
uint8_t gatt_client_write_long_characteristic_descriptor(btstack_packet_handler_t callback, hci_con_handle_t con_handle, gatt_client_characteristic_descriptor_t * descriptor, uint16_t value_length, uint8_t * value){
return gatt_client_write_long_characteristic_descriptor_using_descriptor_handle(callback, con_handle, descriptor->handle, value_length, value);
}
/**
* @brief -> gatt complete event
*/
uint8_t gatt_client_prepare_write(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint16_t value_length, uint8_t * value){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->attribute_handle = attribute_handle;
gatt_client->attribute_length = value_length;
gatt_client->attribute_offset = offset;
gatt_client->attribute_value = value;
gatt_client->gatt_client_state = P_W2_PREPARE_WRITE_SINGLE;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
/**
* @brief -> gatt complete event
*/
uint8_t gatt_client_execute_write(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->gatt_client_state = P_W2_EXECUTE_PREPARED_WRITE;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
/**
* @brief -> gatt complete event
*/
uint8_t gatt_client_cancel_write(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_request(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
gatt_client->callback = callback;
gatt_client->gatt_client_state = P_W2_CANCEL_PREPARED_WRITE;
gatt_client_run();
return ERROR_CODE_SUCCESS;
}
void gatt_client_deserialize_service(const uint8_t *packet, int offset, gatt_client_service_t * service){
service->start_group_handle = little_endian_read_16(packet, offset);
service->end_group_handle = little_endian_read_16(packet, offset + 2);
reverse_128(&packet[offset + 4], service->uuid128);
if (uuid_has_bluetooth_prefix(service->uuid128)){
service->uuid16 = big_endian_read_32(service->uuid128, 0);
} else {
service->uuid16 = 0;
}
}
void gatt_client_deserialize_characteristic(const uint8_t * packet, int offset, gatt_client_characteristic_t * characteristic){
characteristic->start_handle = little_endian_read_16(packet, offset);
characteristic->value_handle = little_endian_read_16(packet, offset + 2);
characteristic->end_handle = little_endian_read_16(packet, offset + 4);
characteristic->properties = little_endian_read_16(packet, offset + 6);
reverse_128(&packet[offset+8], characteristic->uuid128);
if (uuid_has_bluetooth_prefix(characteristic->uuid128)){
characteristic->uuid16 = big_endian_read_32(characteristic->uuid128, 0);
} else {
characteristic->uuid16 = 0;
}
}
void gatt_client_deserialize_characteristic_descriptor(const uint8_t * packet, int offset, gatt_client_characteristic_descriptor_t * descriptor){
descriptor->handle = little_endian_read_16(packet, offset);
reverse_128(&packet[offset+2], descriptor->uuid128);
if (uuid_has_bluetooth_prefix(descriptor->uuid128)){
descriptor->uuid16 = big_endian_read_32(descriptor->uuid128, 0);
} else {
descriptor->uuid16 = 0;
}
}
void gatt_client_send_mtu_negotiation(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return;
}
if (gatt_client->mtu_state == MTU_AUTO_EXCHANGE_DISABLED){
gatt_client->callback = callback;
gatt_client->mtu_state = SEND_MTU_EXCHANGE;
gatt_client_run();
}
}
uint8_t gatt_client_request_to_write_without_response(btstack_context_callback_registration_t * callback_registration, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
bool added = btstack_linked_list_add_tail(&gatt_client->write_without_response_requests, (btstack_linked_item_t*) callback_registration);
if (added == false){
return ERROR_CODE_COMMAND_DISALLOWED;
} else {
att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
return ERROR_CODE_SUCCESS;
}
}
uint8_t gatt_client_request_to_send_gatt_query(btstack_context_callback_registration_t * callback_registration, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
bool added = btstack_linked_list_add_tail(&gatt_client->query_requests, (btstack_linked_item_t*) callback_registration);
if (added == false){
return ERROR_CODE_COMMAND_DISALLOWED;
} else {
gatt_client_notify_can_send_query(gatt_client);
return ERROR_CODE_SUCCESS;
}
}
uint8_t gatt_client_request_can_write_without_response_event(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
if (gatt_client->write_without_response_callback != NULL){
return GATT_CLIENT_IN_WRONG_STATE;
}
gatt_client->write_without_response_callback = callback;
att_dispatch_client_request_can_send_now_event(gatt_client->con_handle);
return ERROR_CODE_SUCCESS;
}
#if defined(ENABLE_GATT_OVER_CLASSIC) || defined(ENABLE_GATT_OVER_EATT)
#include "hci_event.h"
static const hci_event_t gatt_client_connected = {
GATT_EVENT_CONNECTED, 0, "1BH"
};
static const hci_event_t gatt_client_disconnected = {
GATT_EVENT_DISCONNECTED, 0, "H"
};
#endif
#ifdef ENABLE_GATT_OVER_CLASSIC
#include "bluetooth_psm.h"
// single active SDP query
static gatt_client_t * gatt_client_classic_active_sdp_query;
// macos protocol descriptor list requires 16 bytes
static uint8_t gatt_client_classic_sdp_buffer[32];
static gatt_client_t * gatt_client_get_context_for_classic_addr(bd_addr_t addr){
btstack_linked_item_t *it;
for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
gatt_client_t * gatt_client = (gatt_client_t *) it;
if (memcmp(gatt_client->addr, addr, 6) == 0){
return gatt_client;
}
}
return NULL;
}
static gatt_client_t * gatt_client_get_context_for_l2cap_cid(uint16_t l2cap_cid){
btstack_linked_item_t *it;
for (it = (btstack_linked_item_t *) gatt_client_connections; it != NULL; it = it->next){
gatt_client_t * gatt_client = (gatt_client_t *) it;
if (gatt_client->l2cap_cid == l2cap_cid){
return gatt_client;
}
}
return NULL;
}
static void gatt_client_classic_handle_connected(gatt_client_t * gatt_client, uint8_t status){
bd_addr_t addr;
// cppcheck-suppress uninitvar ; addr is reported as uninitialized although it's the destination of the memcpy
memcpy(addr, gatt_client->addr, 6);
hci_con_handle_t con_handle = gatt_client->con_handle;
btstack_packet_handler_t callback = gatt_client->callback;
if (status != ERROR_CODE_SUCCESS){
btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
btstack_memory_gatt_client_free(gatt_client);
}
uint8_t buffer[20];
uint16_t len = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &gatt_client_connected, status, addr,
con_handle);
(*callback)(HCI_EVENT_PACKET, 0, buffer, len);
}
static void gatt_client_classic_handle_disconnected(gatt_client_t * gatt_client){
gatt_client_report_error_if_pending(gatt_client, ATT_ERROR_HCI_DISCONNECT_RECEIVED);
gatt_client_timeout_stop(gatt_client);
hci_con_handle_t con_handle = gatt_client->con_handle;
btstack_packet_handler_t callback = gatt_client->callback;
btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) gatt_client);
btstack_memory_gatt_client_free(gatt_client);
uint8_t buffer[20];
uint16_t len = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &gatt_client_disconnected, con_handle);
(*callback)(HCI_EVENT_PACKET, 0, buffer, len);
}
static void gatt_client_l2cap_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
gatt_client_t * gatt_client = NULL;
uint8_t status;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case L2CAP_EVENT_CHANNEL_OPENED:
status = l2cap_event_channel_opened_get_status(packet);
gatt_client = gatt_client_get_context_for_l2cap_cid(l2cap_event_channel_opened_get_local_cid(packet));
btstack_assert(gatt_client != NULL);
// if status != 0, gatt_client will be discarded
gatt_client->gatt_client_state = P_READY;
gatt_client->con_handle = l2cap_event_channel_opened_get_handle(packet);
gatt_client->mtu = l2cap_event_channel_opened_get_remote_mtu(packet);
gatt_client_classic_handle_connected(gatt_client, status);
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
gatt_client = gatt_client_get_context_for_l2cap_cid(l2cap_event_channel_closed_get_local_cid(packet));
gatt_client_classic_handle_disconnected(gatt_client);
break;
default:
break;
}
break;
case L2CAP_DATA_PACKET:
gatt_client = gatt_client_get_context_for_l2cap_cid(channel);
btstack_assert(gatt_client != NULL);
gatt_client_handle_att_response(gatt_client, packet, size);
gatt_client_run();
break;
default:
break;
}
}
static void gatt_client_handle_sdp_client_query_attribute_value(gatt_client_t * connection, uint8_t *packet){
des_iterator_t des_list_it;
des_iterator_t prot_it;
if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= sizeof(gatt_client_classic_sdp_buffer)) {
gatt_client_classic_sdp_buffer[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) {
switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
for (des_iterator_init(&des_list_it, gatt_client_classic_sdp_buffer); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) {
uint8_t *des_element;
uint8_t *element;
uint32_t uuid;
if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
des_element = des_iterator_get_element(&des_list_it);
des_iterator_init(&prot_it, des_element);
element = des_iterator_get_element(&prot_it);
if (de_get_element_type(element) != DE_UUID) continue;
uuid = de_get_uuid32(element);
des_iterator_next(&prot_it);
// we assume that the even if there are both roles supported, remote device uses the same psm and avdtp version for both
switch (uuid){
case BLUETOOTH_PROTOCOL_L2CAP:
if (!des_iterator_has_more(&prot_it)) continue;
de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->l2cap_psm);
break;
default:
break;
}
}
break;
default:
break;
}
}
}
}
static void gatt_client_classic_sdp_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){
gatt_client_t * gatt_client = gatt_client_classic_active_sdp_query;
btstack_assert(gatt_client != NULL);
uint8_t status;
// TODO: handle sdp events, get l2cap psm
switch (hci_event_packet_get_type(packet)){
case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
gatt_client_handle_sdp_client_query_attribute_value(gatt_client, packet);
// TODO:
return;
case SDP_EVENT_QUERY_COMPLETE:
status = sdp_event_query_complete_get_status(packet);
gatt_client_classic_active_sdp_query = NULL;
log_info("l2cap psm: %0x, status %02x", gatt_client->l2cap_psm, status);
if (status != ERROR_CODE_SUCCESS) break;
if (gatt_client->l2cap_psm == 0) {
status = SDP_SERVICE_NOT_FOUND;
break;
}
break;
default:
btstack_assert(false);
return;
}
// done
if (status == ERROR_CODE_SUCCESS){
gatt_client->gatt_client_state = P_W4_L2CAP_CONNECTION;
status = l2cap_create_channel(gatt_client_l2cap_handler, gatt_client->addr, gatt_client->l2cap_psm, 0xffff,
&gatt_client->l2cap_cid);
}
if (status != ERROR_CODE_SUCCESS) {
gatt_client_classic_handle_connected(gatt_client, status);
}
}
static void gatt_client_classic_sdp_start(void * context){
gatt_client_classic_active_sdp_query = (gatt_client_t *) context;
gatt_client_classic_active_sdp_query->gatt_client_state = P_W4_SDP_QUERY;
sdp_client_query_uuid16(gatt_client_classic_sdp_handler, gatt_client_classic_active_sdp_query->addr, ORG_BLUETOOTH_SERVICE_GENERIC_ATTRIBUTE);
}
uint8_t gatt_client_classic_connect(btstack_packet_handler_t callback, bd_addr_t addr){
gatt_client_t * gatt_client = gatt_client_get_context_for_classic_addr(addr);
if (gatt_client != NULL){
return ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS;
}
gatt_client = btstack_memory_gatt_client_get();
if (gatt_client == NULL){
return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
}
// init state
gatt_client->bearer_type = ATT_BEARER_UNENHANCED_CLASSIC;
gatt_client->con_handle = HCI_CON_HANDLE_INVALID;
memcpy(gatt_client->addr, addr, 6);
gatt_client->mtu = ATT_DEFAULT_MTU;
gatt_client->security_level = LEVEL_0;
gatt_client->mtu_state = MTU_AUTO_EXCHANGE_DISABLED;
gatt_client->gatt_client_state = P_W2_SDP_QUERY;
gatt_client->sdp_query_request.callback = &gatt_client_classic_sdp_start;
gatt_client->sdp_query_request.context = gatt_client;
gatt_client->callback = callback;
#ifdef ENABLE_GATT_OVER_EATT
gatt_client->eatt_state = GATT_CLIENT_EATT_IDLE;
#endif
btstack_linked_list_add(&gatt_client_connections, (btstack_linked_item_t*)gatt_client);
sdp_client_register_query_callback(&gatt_client->sdp_query_request);
return ERROR_CODE_SUCCESS;
}
uint8_t gatt_client_classic_disconnect(btstack_packet_handler_t callback, hci_con_handle_t con_handle){
gatt_client_t * gatt_client = gatt_client_get_context_for_handle(con_handle);
if (gatt_client == NULL){
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
gatt_client->callback = callback;
return l2cap_disconnect(gatt_client->l2cap_cid);
}
#endif
#ifdef ENABLE_GATT_OVER_EATT
#define MAX_NR_EATT_CHANNELS 5
static void gatt_client_le_enhanced_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void gatt_client_eatt_finalize(gatt_client_t * gatt_client) {
// free eatt clients
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &gatt_client_connections);
while (btstack_linked_list_iterator_has_next(&it)) {
gatt_client_t *eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
btstack_linked_list_iterator_remove(&it);
btstack_memory_gatt_client_free(eatt_client);
}
}
// all channels connected
static void gatt_client_le_enhanced_handle_connected(gatt_client_t * gatt_client, uint8_t status) {
if (status == ERROR_CODE_SUCCESS){
gatt_client->eatt_state = GATT_CLIENT_EATT_READY;
} else {
gatt_client_eatt_finalize(gatt_client);
gatt_client->eatt_state = GATT_CLIENT_EATT_IDLE;
}
uint8_t buffer[20];
uint16_t len = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &gatt_client_connected, status, gatt_client->addr,
gatt_client->con_handle);
(*gatt_client->callback)(HCI_EVENT_PACKET, 0, buffer, len);
}
// single channel disconnected
static void gatt_client_le_enhanced_handle_ecbm_disconnected(gatt_client_t * gatt_client, gatt_client_t * eatt_client) {
if (gatt_client->eatt_state == GATT_CLIENT_EATT_READY) {
// report error
gatt_client_report_error_if_pending(eatt_client, ATT_ERROR_HCI_DISCONNECT_RECEIVED);
// free memory
btstack_linked_list_remove(&gatt_client->eatt_clients, (btstack_linked_item_t *) eatt_client);
btstack_memory_gatt_client_free(eatt_client);
// report disconnected if last channel closed
if (btstack_linked_list_empty(&gatt_client->eatt_clients)){
// emit disconnected when last eatt client is gone
uint8_t buffer[20];
uint16_t len = hci_event_create_from_template_and_arguments(buffer, sizeof(buffer), &gatt_client_disconnected, gatt_client->con_handle);
(*gatt_client->callback)(HCI_EVENT_PACKET, 0, buffer, len);
}
}
}
static gatt_client_t * gatt_client_le_enhanced_get_context_for_l2cap_cid(uint16_t l2cap_cid, gatt_client_t ** out_eatt_client){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &gatt_client_connections);
while (btstack_linked_list_iterator_has_next(&it)) {
gatt_client_t * gatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
btstack_linked_list_iterator_t it2;
btstack_linked_list_iterator_init(&it2, &gatt_client->eatt_clients);
while (btstack_linked_list_iterator_has_next(&it2)) {
gatt_client_t * eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it2);
if (eatt_client->l2cap_cid == l2cap_cid){
*out_eatt_client = eatt_client;
return gatt_client;
}
}
}
return NULL;
}
static void gatt_client_le_enhanced_setup_l2cap_channel(gatt_client_t * gatt_client){
uint8_t num_channels = gatt_client->eatt_num_clients;
// setup channels
uint16_t buffer_size_per_client = gatt_client->eatt_storage_size / num_channels;
uint16_t max_mtu = (buffer_size_per_client - REPORT_PREBUFFER_HEADER) / 2;
uint8_t * receive_buffers[MAX_NR_EATT_CHANNELS];
uint16_t new_cids[MAX_NR_EATT_CHANNELS];
memset(gatt_client->eatt_storage_buffer, 0, gatt_client->eatt_storage_size);
uint8_t i;
for (i=0;i<gatt_client->eatt_num_clients; i++){
receive_buffers[i] = &gatt_client->eatt_storage_buffer[REPORT_PREBUFFER_HEADER];
gatt_client->eatt_storage_buffer += REPORT_PREBUFFER_HEADER + max_mtu;
}
log_info("%u EATT clients with receive buffer size %u", gatt_client->eatt_num_clients, buffer_size_per_client);
uint8_t status = l2cap_ecbm_create_channels(&gatt_client_le_enhanced_packet_handler,
gatt_client->con_handle,
gatt_client->security_level,
BLUETOOTH_PSM_EATT, num_channels,
L2CAP_LE_AUTOMATIC_CREDITS,
buffer_size_per_client,
receive_buffers,
new_cids);
if (status == ERROR_CODE_SUCCESS){
i = 0;
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &gatt_client->eatt_clients);
while (btstack_linked_list_iterator_has_next(&it)) {
gatt_client_t *new_eatt_client = (gatt_client_t *) btstack_linked_list_iterator_next(&it);
// init state with new cid and transmit buffer
new_eatt_client->bearer_type = ATT_BEARER_ENHANCED_LE;
new_eatt_client->con_handle = gatt_client->con_handle;
new_eatt_client->mtu = 64;
new_eatt_client->security_level = LEVEL_0;
new_eatt_client->mtu_state = MTU_AUTO_EXCHANGE_DISABLED;
new_eatt_client->gatt_client_state = P_W4_L2CAP_CONNECTION;
new_eatt_client->l2cap_cid = new_cids[i];
new_eatt_client->eatt_storage_buffer = gatt_client->eatt_storage_buffer;
gatt_client->eatt_storage_buffer += max_mtu;
i++;
}
gatt_client->eatt_state = GATT_CLIENT_EATT_L2CAP_SETUP;
} else {
gatt_client_le_enhanced_handle_connected(gatt_client, status);
}
}
static void gatt_client_le_enhanced_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
gatt_client_t *gatt_client;
gatt_client_t *eatt_client;
hci_con_handle_t con_handle;
uint16_t l2cap_cid;
uint8_t status;
gatt_client_characteristic_t characteristic;
gatt_client_service_t service;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case GATT_EVENT_SERVICE_QUERY_RESULT:
con_handle = gatt_event_service_query_result_get_handle(packet);
gatt_client = gatt_client_get_context_for_handle(con_handle);
btstack_assert(gatt_client != NULL);
btstack_assert(gatt_client->eatt_state == GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W4_DONE);
gatt_event_service_query_result_get_service(packet, &service);
gatt_client->gatt_service_start_group_handle = service.start_group_handle;
gatt_client->gatt_service_end_group_handle = service.end_group_handle;
break;
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
con_handle = gatt_event_characteristic_value_query_result_get_handle(packet);
gatt_client = gatt_client_get_context_for_handle(con_handle);
btstack_assert(gatt_client != NULL);
btstack_assert(gatt_client->eatt_state == GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W4_DONE);
if (gatt_event_characteristic_value_query_result_get_value_length(packet) >= 1) {
gatt_client->gatt_server_supported_features = gatt_event_characteristic_value_query_result_get_value(packet)[0];
}
break;
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
con_handle = gatt_event_characteristic_query_result_get_handle(packet);
gatt_client = gatt_client_get_context_for_handle(con_handle);
btstack_assert(gatt_client != NULL);
btstack_assert(gatt_client->eatt_state == GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W4_DONE);
gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
gatt_client->gatt_client_supported_features_handle = characteristic.value_handle;
break;
case GATT_EVENT_QUERY_COMPLETE:
con_handle = gatt_event_query_complete_get_handle(packet);
gatt_client = gatt_client_get_context_for_handle(con_handle);
btstack_assert(gatt_client != NULL);
switch (gatt_client->eatt_state){
case GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W4_DONE:
if (gatt_client->gatt_service_start_group_handle == 0){
gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
} else {
gatt_client->eatt_state = GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W2_SEND;
}
break;
case GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W4_DONE:
if ((gatt_client->gatt_server_supported_features & 1) == 0) {
gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
} else {
gatt_client->eatt_state = GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W2_SEND;
}
break;
case GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W4_DONE:
if (gatt_client->gatt_client_supported_features_handle == 0){
gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
} else {
gatt_client->eatt_state = GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W2_SEND;
}
break;
case GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W4_DONE:
gatt_client_le_enhanced_setup_l2cap_channel(gatt_client);
break;
default:
break;
}
break;
case L2CAP_EVENT_ECBM_CHANNEL_OPENED:
l2cap_cid = l2cap_event_ecbm_channel_opened_get_local_cid(packet);
gatt_client = gatt_client_le_enhanced_get_context_for_l2cap_cid(l2cap_cid, &eatt_client);
btstack_assert(gatt_client != NULL);
btstack_assert(eatt_client != NULL);
btstack_assert(eatt_client->gatt_client_state == P_W4_L2CAP_CONNECTION);
status = l2cap_event_channel_opened_get_status(packet);
if (status == ERROR_CODE_SUCCESS){
eatt_client->gatt_client_state = P_READY;
eatt_client->mtu = l2cap_event_channel_opened_get_remote_mtu(packet);
} else {
gatt_client_le_enhanced_handle_ecbm_disconnected(gatt_client, eatt_client);
}
// all channels opened?
gatt_client->eatt_num_clients--;
if (gatt_client->eatt_num_clients == 0){
gatt_client_le_enhanced_handle_connected(gatt_client, ERROR_CODE_SUCCESS);
}
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
l2cap_cid = l2cap_event_channel_closed_get_local_cid(packet);
gatt_client = gatt_client_le_enhanced_get_context_for_l2cap_cid(l2cap_cid, &eatt_client);
btstack_assert(gatt_client != NULL);
btstack_assert(eatt_client != NULL);
gatt_client_le_enhanced_handle_ecbm_disconnected(gatt_client, eatt_client);
break;
default:
break;
}
break;
case L2CAP_DATA_PACKET:
gatt_client = gatt_client_le_enhanced_get_context_for_l2cap_cid(channel, &eatt_client);
btstack_assert(gatt_client != NULL);
btstack_assert(eatt_client != NULL);
gatt_client_handle_att_response(eatt_client, packet, size);
gatt_client_run();
break;
default:
break;
}
}
static bool gatt_client_le_enhanced_handle_can_send_query(gatt_client_t * gatt_client){
uint8_t status = ERROR_CODE_SUCCESS;
uint8_t gatt_client_supported_features = 0x06; // eatt + multiple value notifications
switch (gatt_client->eatt_state){
case GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W2_SEND:
gatt_client->gatt_service_start_group_handle = 0;
gatt_client->eatt_state = GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W4_DONE;
status = gatt_client_discover_primary_services_by_uuid16(&gatt_client_le_enhanced_packet_handler,
gatt_client->con_handle,
ORG_BLUETOOTH_SERVICE_GENERIC_ATTRIBUTE);
break;
case GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W2_SEND:
gatt_client->gatt_server_supported_features = 0;
gatt_client->eatt_state = GATT_CLIENT_EATT_READ_SERVER_SUPPORTED_FEATURES_W4_DONE;
status = gatt_client_read_value_of_characteristics_by_uuid16(&gatt_client_le_enhanced_packet_handler,
gatt_client->con_handle,
gatt_client->gatt_service_start_group_handle,
gatt_client->gatt_service_end_group_handle,
ORG_BLUETOOTH_CHARACTERISTIC_SERVER_SUPPORTED_FEATURES);
return true;
case GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W2_SEND:
gatt_client->gatt_client_supported_features_handle = 0;
gatt_client->eatt_state = GATT_CLIENT_EATT_FIND_CLIENT_SUPPORTED_FEATURES_W4_DONE;
status = gatt_client_discover_characteristics_for_handle_range_by_uuid16(&gatt_client_le_enhanced_packet_handler,
gatt_client->con_handle,
gatt_client->gatt_service_start_group_handle,
gatt_client->gatt_service_end_group_handle,
ORG_BLUETOOTH_CHARACTERISTIC_CLIENT_SUPPORTED_FEATURES);
return true;
case GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W2_SEND:
gatt_client->eatt_state = GATT_CLIENT_EATT_WRITE_ClIENT_SUPPORTED_FEATURES_W4_DONE;
status = gatt_client_write_value_of_characteristic(&gatt_client_le_enhanced_packet_handler, gatt_client->con_handle,
gatt_client->gatt_client_supported_features_handle, 1,
&gatt_client_supported_features);
return true;
default:
break;
}
btstack_assert(status == ERROR_CODE_SUCCESS);
UNUSED(status);
return false;
}
uint8_t gatt_client_le_enhanced_connect(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint8_t num_channels, uint8_t * storage_buffer, uint16_t storage_size) {
gatt_client_t * gatt_client;
uint8_t status = gatt_client_provide_context_for_handle(con_handle, &gatt_client);
if (status != ERROR_CODE_SUCCESS){
return status;
}
if (gatt_client->eatt_state != GATT_CLIENT_EATT_IDLE){
return ERROR_CODE_COMMAND_DISALLOWED;
}
// need one buffer for sending and one for receiving. Receiving includes pre-buffer for reports
uint16_t buffer_size_per_client = storage_size / num_channels;
uint16_t max_mtu = (buffer_size_per_client - REPORT_PREBUFFER_HEADER) / 2;
if (max_mtu < 64) {
return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
}
if ((num_channels == 0) || (num_channels > MAX_NR_EATT_CHANNELS)){
return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
}
// create max num_channel eatt clients
uint8_t i;
btstack_linked_list_t eatt_clients = NULL;
for (i=0;i<num_channels;i++) {
gatt_client_t * new_gatt_client = btstack_memory_gatt_client_get();
if (new_gatt_client == NULL) {
break;
}
btstack_linked_list_add(&eatt_clients, (btstack_linked_item_t*)new_gatt_client);
}
if (i != num_channels){
while (true){
gatt_client = (gatt_client_t *) btstack_linked_list_pop(&eatt_clients);
if (gatt_client == NULL) {
break;
}
btstack_memory_gatt_client_free(gatt_client);
}
return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
}
gatt_client->callback = callback;
gatt_client->eatt_num_clients = num_channels;
gatt_client->eatt_storage_buffer = storage_buffer;
gatt_client->eatt_storage_size = storage_size;
gatt_client->eatt_clients = eatt_clients;
gatt_client->eatt_state = GATT_CLIENT_EATT_DISCOVER_GATT_SERVICE_W2_SEND;
gatt_client_notify_can_send_query(gatt_client);
return ERROR_CODE_SUCCESS;
}
#endif
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
void gatt_client_att_packet_handler_fuzz(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){
gatt_client_att_packet_handler(packet_type, handle, packet, size);
}
uint8_t gatt_client_get_client(hci_con_handle_t con_handle, gatt_client_t ** out_gatt_client){
uint8_t status = gatt_client_provide_context_for_handle(con_handle, out_gatt_client);
return status;
}
#endif