Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
Diff: features/FEATURE_BLE/source/generic/GenericGattClient.tpp
- Revision:
- 0:5b88d5760320
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/features/FEATURE_BLE/source/generic/GenericGattClient.tpp Tue Dec 17 23:23:45 2019 +0000
@@ -0,0 +1,1524 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2017-2017 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BLERoles.h"
+
+#if BLE_FEATURE_GATT_CLIENT
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ble/pal/AttServerMessage.h"
+#include "ble/GattClient.h"
+#include <ble/DiscoveredService.h>
+#include <ble/DiscoveredCharacteristic.h>
+#include "ble/generic/GenericGattClient.h"
+#include "ble/blecommon.h"
+#include "ble/BLEInstanceBase.h"
+#include <algorithm>
+
+using ble::pal::AttServerMessage;
+using ble::pal::AttReadResponse;
+using ble::pal::AttReadBlobResponse;
+using ble::pal::AttReadByTypeResponse;
+using ble::pal::AttReadByGroupTypeResponse;
+using ble::pal::AttFindByTypeValueResponse;
+using ble::pal::AttErrorResponse;
+using ble::pal::AttributeOpcode;
+using ble::pal::AttWriteResponse;
+using ble::pal::AttPrepareWriteResponse;
+using ble::pal::AttExecuteWriteResponse;
+using ble::pal::AttHandleValueIndication;
+using ble::pal::AttHandleValueNotification;
+using ble::pal::AttFindInformationResponse;
+
+#define PREPARE_WRITE_HEADER_LENGTH 5
+#define WRITE_HEADER_LENGTH 3
+#define CMAC_LENGTH 8
+#define MAC_COUNTER_LENGTH 4
+
+namespace ble {
+namespace generic {
+
+/*
+ * Type of procedures which can be launched by the client.
+ */
+enum procedure_type_t {
+ COMPLETE_DISCOVERY_PROCEDURE,
+ READ_PROCEDURE,
+ WRITE_PROCEDURE,
+ DESCRIPTOR_DISCOVERY_PROCEDURE
+};
+
+
+/*
+ * Base class for a procedure control block
+ */
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+struct GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::ProcedureControlBlock {
+ /*
+ * Base constructor for procedure control block.
+ */
+ ProcedureControlBlock(procedure_type_t type, connection_handle_t handle) :
+ type(type), connection_handle(handle), next(NULL) { }
+
+ virtual ~ProcedureControlBlock() { }
+
+ /*
+ * Entry point of the control block stack machine.
+ */
+ virtual void handle(GenericGattClient* client, const AttServerMessage& message) = 0;
+
+ /*
+ * Function call in case of timeout
+ */
+ virtual void handle_timeout_error(GenericGattClient* client) = 0;
+
+ /**
+ * Function called when the procedure is aborted
+ */
+ virtual void abort(GenericGattClient *client) = 0;
+
+ procedure_type_t type;
+ connection_handle_t connection_handle;
+ ProcedureControlBlock* next;
+};
+
+
+/*
+ * Procedure control block for the discovery process.
+ */
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+struct GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::DiscoveryControlBlock : public ProcedureControlBlock {
+ using ProcedureControlBlock::connection_handle;
+
+ DiscoveryControlBlock(
+ connection_handle_t handle,
+ ServiceDiscovery::ServiceCallback_t service_callback,
+ ServiceDiscovery::CharacteristicCallback_t characteristic_callback,
+ UUID matching_service_uuid,
+ UUID matching_characteristic_uuid
+ ) : ProcedureControlBlock(COMPLETE_DISCOVERY_PROCEDURE, handle),
+ service_callback(service_callback),
+ characteristic_callback(characteristic_callback),
+ matching_service_uuid(matching_service_uuid),
+ matching_characteristic_uuid(matching_characteristic_uuid),
+ services_discovered(NULL),
+ done(false) {
+ }
+
+ virtual ~DiscoveryControlBlock() {
+ while(services_discovered) {
+ service_t* tmp = services_discovered->next;
+ delete services_discovered;
+ services_discovered = tmp;
+ }
+ }
+
+ virtual void handle_timeout_error(GenericGattClient* client) {
+ terminate(client);
+ }
+
+ virtual void abort(GenericGattClient *client) {
+ terminate(client);
+ }
+
+ virtual void handle(GenericGattClient* client, const AttServerMessage& message) {
+ // if end of discovery has been requested, ends it immediately
+ if (done) {
+ terminate(client);
+ return;
+ }
+
+ switch(message.opcode) {
+ case AttributeOpcode::READ_BY_GROUP_TYPE_RESPONSE:
+ handle_service_discovered(
+ client, static_cast<const AttReadByGroupTypeResponse&>(message)
+ );
+ break;
+ case AttributeOpcode::FIND_BY_VALUE_TYPE_RESPONSE:
+ handle_service_discovered(
+ client, static_cast<const AttFindByTypeValueResponse&>(message)
+ );
+ break;
+ case AttributeOpcode::READ_BY_TYPE_RESPONSE:
+ handle_characteristic_discovered(
+ client, static_cast<const AttReadByTypeResponse&>(message)
+ );
+ break;
+ case AttributeOpcode::ERROR_RESPONSE: {
+ const AttErrorResponse& error = static_cast<const AttErrorResponse&>(message);
+ if (error.error_code != AttErrorResponse::ATTRIBUTE_NOT_FOUND) {
+ terminate(client);
+ return;
+ }
+
+ switch (error.request_opcode) {
+ case AttributeOpcode::READ_BY_GROUP_TYPE_REQUEST:
+ case AttributeOpcode::FIND_BY_TYPE_VALUE_REQUEST:
+ start_characteristic_discovery(client);
+ break;
+ case AttributeOpcode::READ_BY_TYPE_REQUEST:
+ handle_all_characteristics_discovered(client);
+ break;
+ default:
+ // error
+ break;
+ }
+ } break;
+ default:
+ // error
+ break;
+ }
+ }
+
+ template<typename Response>
+ void handle_service_discovered(GenericGattClient* client, const Response& response) {
+ if (!response.size()) {
+ terminate(client);
+ return;
+ }
+
+ uint16_t end_handle = 0x0000;
+ for (size_t i = 0; i < response.size(); ++i) {
+ uint16_t start_handle = get_start_handle(response[i]);
+ end_handle = get_end_handle(response[i]);
+ UUID uuid = get_uuid(response[i]);
+
+ if (!characteristic_callback) {
+ DiscoveredService discovered_service;
+ discovered_service.setup(uuid, start_handle, end_handle);
+ service_callback(&discovered_service);
+ } else {
+ service_t* discovered_service = new (std::nothrow) service_t(
+ start_handle, end_handle, uuid
+ );
+
+ if (discovered_service == NULL) {
+ terminate(client);
+ return;
+ }
+
+ insert_service(discovered_service);
+ }
+ }
+
+ if (end_handle == 0xFFFF) {
+ start_characteristic_discovery(client);
+ } else {
+ ble_error_t err = client->_pal_client->discover_primary_service(
+ connection_handle, end_handle + 1
+ );
+
+ if (err) {
+ terminate(client);
+ }
+ }
+ }
+
+ void start_characteristic_discovery(GenericGattClient* client) {
+ if (!services_discovered) {
+ terminate(client);
+ return;
+ }
+
+ if (!characteristic_callback) {
+ terminate(client);
+ return;
+ }
+
+ if (service_callback) {
+ DiscoveredService discovered_service;
+ discovered_service.setup(
+ services_discovered->uuid,
+ services_discovered->begin,
+ services_discovered->end
+ );
+ service_callback(&discovered_service);
+ }
+
+ last_characteristic = characteristic_t();
+ client->_pal_client->discover_characteristics_of_a_service(
+ connection_handle,
+ attribute_handle_range(
+ services_discovered->begin,
+ services_discovered->end
+ )
+ );
+ }
+
+ void handle_characteristic_discovered(GenericGattClient* client, const AttReadByTypeResponse& response) {
+ for (size_t i = 0; i < response.size(); ++i) {
+ if (last_characteristic.is_valid() == false) {
+ last_characteristic.set_last_handle(response[i].handle - 1);
+ if (matching_characteristic_uuid == UUID()
+ || last_characteristic.getUUID() == matching_characteristic_uuid) {
+ characteristic_callback(&last_characteristic);
+ }
+ }
+
+ last_characteristic = characteristic_t(
+ client, connection_handle, response[i].handle, response[i].value
+ );
+ }
+
+ // check if all the characteristics of the service has been discovered
+ if (last_characteristic.getValueHandle() == services_discovered->end) {
+ handle_all_characteristics_discovered(client);
+ } else {
+ ble_error_t err = client->_pal_client->discover_characteristics_of_a_service(
+ connection_handle,
+ attribute_handle_range(
+ last_characteristic.getValueHandle() + 1,
+ services_discovered->end
+ )
+ );
+
+ if (err) {
+ terminate(client);
+ }
+ }
+ }
+
+ void handle_all_characteristics_discovered(GenericGattClient* client) {
+ if (last_characteristic.is_valid() == false) {
+ if (matching_characteristic_uuid == UUID()
+ || matching_characteristic_uuid == last_characteristic.getUUID()) {
+ last_characteristic.set_last_handle(services_discovered->end);
+ characteristic_callback(&last_characteristic);
+ }
+ }
+
+ service_t* old = services_discovered;
+ services_discovered = services_discovered->next;
+ delete old;
+
+ if (!services_discovered) {
+ terminate(client);
+ } else {
+ start_characteristic_discovery(client);
+ }
+ }
+
+ void terminate(GenericGattClient* client) {
+ // unknown error, terminate the procedure immediately
+ client->remove_control_block(this);
+ connection_handle_t handle = connection_handle;
+ delete this;
+ client->on_termination(handle);
+ }
+
+ uint16_t get_start_handle(const AttReadByGroupTypeResponse::attribute_data_t& data) {
+ return data.group_range.begin;
+ }
+
+ uint16_t get_start_handle(const attribute_handle_range_t& range) {
+ return range.begin;
+ }
+
+ uint16_t get_end_handle(const AttReadByGroupTypeResponse::attribute_data_t& data) {
+ return data.group_range.end;
+ }
+
+ uint16_t get_end_handle(const attribute_handle_range_t& range) {
+ return range.end;
+ }
+
+ UUID get_uuid(const AttReadByGroupTypeResponse::attribute_data_t& data) {
+ if (data.value.size() == 2) {
+ return UUID(data.value[0] | data.value[1] << 8);
+ } else {
+ return UUID(data.value.data(), UUID::LSB);
+ }
+ }
+
+ UUID get_uuid(const attribute_handle_range_t& range) {
+ return matching_service_uuid;
+ }
+
+ struct service_t {
+ service_t(uint16_t begin, uint16_t end, const UUID& uuid) :
+ begin(begin), end(end), uuid(uuid), next(NULL) { }
+ uint16_t begin;
+ uint16_t end;
+ UUID uuid;
+ service_t* next;
+ };
+
+ struct characteristic_t : DiscoveredCharacteristic {
+ characteristic_t() : DiscoveredCharacteristic() {
+ lastHandle = 0x0001;
+ }
+
+ characteristic_t(
+ GattClient* client,
+ connection_handle_t connection_handle,
+ uint16_t decl_handle,
+ const Span<const uint8_t> value
+ ) : DiscoveredCharacteristic() {
+ gattc = client;
+ uuid = get_uuid(value);
+ props = get_properties(value);
+ declHandle = decl_handle;
+ valueHandle = get_value_handle(value);
+ lastHandle = 0x0000;
+ connHandle = connection_handle;
+ }
+
+ static UUID get_uuid(const Span<const uint8_t>& value) {
+ if (value.size() == 5) {
+ return UUID(value[3] | (value[4] << 8));
+ } else {
+ return UUID(value.data() + 3, UUID::LSB);
+ }
+ }
+
+ static DiscoveredCharacteristic::Properties_t get_properties(const Span<const uint8_t>& value) {
+ uint8_t raw_properties = value[0];
+ DiscoveredCharacteristic::Properties_t result;
+ result._broadcast = (raw_properties & (1 << 0)) ? true : false;
+ result._read = (raw_properties & (1 << 1)) ? true : false;
+ result._writeWoResp = (raw_properties & (1 << 2)) ? true : false;
+ result._write = (raw_properties & (1 << 3)) ? true : false;
+ result._notify = (raw_properties & (1 << 4)) ? true : false;
+ result._indicate = (raw_properties & (1 << 5)) ? true : false;
+ result._authSignedWrite = (raw_properties & (1 << 6)) ? true : false;
+ return result;
+ }
+
+ static uint16_t get_value_handle(const Span<const uint8_t>& value) {
+ return value[1] | (value[2] << 8);
+ }
+
+ void set_last_handle(uint16_t last_handle) {
+ lastHandle = last_handle;
+ }
+
+ bool is_valid() const {
+ return lastHandle != 0x0000;
+ }
+ };
+
+ void insert_service(service_t* service) {
+ if (services_discovered == NULL) {
+ services_discovered = service;
+ return;
+ }
+
+ service_t* current = services_discovered;
+ while (current->next) {
+ current = current->next;
+ }
+ current->next = service;
+ }
+
+ ServiceDiscovery::ServiceCallback_t service_callback;
+ ServiceDiscovery::CharacteristicCallback_t characteristic_callback;
+ UUID matching_service_uuid;
+ UUID matching_characteristic_uuid;
+ service_t* services_discovered;
+ characteristic_t last_characteristic;
+ bool done;
+};
+
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+struct GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::ReadControlBlock : public ProcedureControlBlock {
+ using ProcedureControlBlock::connection_handle;
+
+ ReadControlBlock(
+ connection_handle_t connection_handle, uint16_t attribute_handle, uint16_t offset
+ ) : ProcedureControlBlock(READ_PROCEDURE, connection_handle),
+ attribute_handle(attribute_handle),
+ offset(offset), current_offset(offset), data(NULL) {
+ }
+
+ virtual ~ReadControlBlock() {
+ if (data != NULL) {
+ free(data);
+ }
+ }
+
+ virtual void handle_timeout_error(GenericGattClient* client) {
+ GattReadCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ offset,
+ 0, // size of 0
+ NULL, // no data
+ BLE_ERROR_UNSPECIFIED,
+
+ };
+ terminate(client, response);
+ }
+
+ virtual void abort(GenericGattClient *client) {
+ GattReadCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ offset,
+ 0, // size of 0
+ NULL, // no data
+ BLE_ERROR_INVALID_STATE,
+
+ };
+ terminate(client, response);
+ }
+
+ void terminate(GenericGattClient* client, const GattReadCallbackParams& response) {
+ client->remove_control_block(this);
+ client->processReadResponse(&response);
+ delete this;
+ }
+
+ virtual void handle(GenericGattClient* client, const AttServerMessage& message) {
+ switch(message.opcode) {
+ case AttributeOpcode::ERROR_RESPONSE:
+ handle_error(client, static_cast<const AttErrorResponse&>(message));
+ break;
+
+ case AttributeOpcode::READ_RESPONSE:
+ handle_read_response(client, static_cast<const AttReadResponse&>(message));
+ break;
+
+ case AttributeOpcode::READ_BLOB_RESPONSE:
+ handle_read_response(client, static_cast<const AttReadBlobResponse&>(message));
+ break;
+
+ default: {
+ // should not happen, terminate the procedure and notify client with an error
+ // in such case
+ GattReadCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ AttErrorResponse::UNLIKELY_ERROR,
+ 0, // size of 0
+ NULL, // no data
+ BLE_ERROR_UNSPECIFIED,
+
+ };
+ terminate(client, response);
+ } break;
+ }
+ }
+
+ template<typename ResponseType>
+ void handle_read_response(GenericGattClient* client, const ResponseType& read_response) {
+ uint16_t mtu_size = client->get_mtu(connection_handle);
+
+ // end of responses ?
+ if ((uint16_t) read_response.size() < (mtu_size - 1)) {
+ GattReadCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ offset,
+ 0, // size of 0
+ NULL, // no data
+ BLE_ERROR_NONE,
+ };
+
+ // is it the first response, or is there any other response already
+ // in the object ?
+ if (data == NULL) {
+ response.len = (uint16_t) read_response.size();
+ response.data = read_response.data();
+ } else {
+ // copy the data in the existing buffer
+ memcpy(data + (current_offset - offset), read_response.data(), read_response.size());
+ response.len = (current_offset + read_response.size()) - offset;
+ response.data = data;
+ }
+ terminate(client, response);
+ } else {
+ // allocation which will contain the response data plus the next one.
+ data = (uint8_t*) realloc(data, (current_offset - offset) + ((mtu_size - 1) * 2));
+ if (data == NULL) {
+ GattReadCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ offset,
+ AttErrorResponse::INSUFFICIENT_RESOURCES,
+ NULL,
+ BLE_ERROR_NO_MEM,
+ };
+ terminate(client, response);
+ return;
+ }
+
+ memcpy(data + (current_offset - offset), read_response.data(), read_response.size());
+ current_offset = current_offset + read_response.size();
+ ble_error_t err = client->_pal_client->read_attribute_blob(
+ connection_handle,
+ attribute_handle,
+ current_offset
+ );
+
+ if (err) {
+ GattReadCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ AttErrorResponse::UNLIKELY_ERROR,
+ 0, // size of 0
+ NULL, // no data
+ BLE_ERROR_UNSPECIFIED,
+
+ };
+ terminate(client, response);
+ }
+ }
+ }
+
+ void handle_error(GenericGattClient* client, const AttErrorResponse& error) {
+ ble_error_t status = BLE_ERROR_UNSPECIFIED;
+
+ switch (error.error_code) {
+ case AttErrorResponse::INVALID_HANDLE:
+ status = BLE_ERROR_INVALID_PARAM;
+ break;
+ case AttErrorResponse::INSUFFICIENT_AUTHORIZATION:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::INSUFFICIENT_AUTHENTICATION:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::INSUFFICIENT_ENCRYPTION_KEY_SIZE:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::INSUFFICIENT_ENCRYPTION:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::READ_NOT_PERMITTED:
+ status = BLE_ERROR_OPERATION_NOT_PERMITTED;
+ break;
+ case AttErrorResponse::INVALID_OFFSET:
+ status = BLE_ERROR_PARAM_OUT_OF_RANGE;
+ break;
+ case AttErrorResponse::ATTRIBUTE_NOT_LONG:
+ status = BLE_ERROR_PARAM_OUT_OF_RANGE;
+ break;
+ default:
+ status = BLE_ERROR_UNSPECIFIED;
+ break;
+ }
+
+ GattReadCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ offset,
+ error.error_code,
+ /* data */ NULL,
+ status
+ };
+
+ terminate(client, response);
+ }
+
+ uint16_t attribute_handle;
+ uint16_t offset;
+ uint16_t current_offset;
+ uint8_t* data;
+};
+
+/*
+ * Control block for the write process
+ */
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+struct GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::WriteControlBlock : public ProcedureControlBlock {
+ using ProcedureControlBlock::connection_handle;
+
+ WriteControlBlock(
+ connection_handle_t connection_handle, uint16_t attribute_handle,
+ uint8_t* data, uint16_t len
+ ) : ProcedureControlBlock(WRITE_PROCEDURE, connection_handle),
+ attribute_handle(attribute_handle), len(len), offset(0), data(data),
+ prepare_success(false), status(BLE_ERROR_UNSPECIFIED), error_code(0xFF) {
+ }
+
+ virtual ~WriteControlBlock() {
+ free(data);
+ }
+
+ virtual void handle_timeout_error(GenericGattClient* client) {
+ GattWriteCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_REQ,
+ BLE_ERROR_UNSPECIFIED,
+ 0x00
+ };
+ terminate(client, response);
+ }
+
+ virtual void abort(GenericGattClient *client) {
+ GattWriteCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_REQ,
+ BLE_ERROR_INVALID_STATE,
+ 0x00
+ };
+ terminate(client, response);
+ }
+
+ void terminate(GenericGattClient* client, const GattWriteCallbackParams& response) {
+ client->remove_control_block(this);
+ client->processWriteResponse(&response);
+ delete this;
+ }
+
+ virtual void handle(GenericGattClient* client, const AttServerMessage& message) {
+ switch(message.opcode) {
+ case AttributeOpcode::ERROR_RESPONSE:
+ handle_error(client, static_cast<const AttErrorResponse&>(message));
+ break;
+
+ case AttributeOpcode::WRITE_RESPONSE:
+ handle_write_response(client, static_cast<const AttWriteResponse&>(message));
+ break;
+
+ case AttributeOpcode::PREPARE_WRITE_RESPONSE:
+ handle_prepare_write_response(client, static_cast<const AttPrepareWriteResponse&>(message));
+ break;
+
+ case AttributeOpcode::EXECUTE_WRITE_RESPONSE:
+ handle_execute_write_response(client, static_cast<const AttExecuteWriteResponse&>(message));
+ break;
+
+ default: {
+ GattWriteCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_REQ,
+ BLE_ERROR_UNSPECIFIED,
+ AttErrorResponse::UNLIKELY_ERROR
+ };
+
+ terminate(client, response);
+ } break;
+ }
+ }
+
+ void handle_write_response(GenericGattClient* client, const AttWriteResponse& write_response) {
+ GattWriteCallbackParams response = {
+ connection_handle, attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_REQ,
+ BLE_ERROR_NONE, 0x00
+ };
+
+ terminate(client, response);
+ }
+
+ void handle_prepare_write_response(GenericGattClient* client, const AttPrepareWriteResponse& write_response) {
+ ble_error_t err = BLE_ERROR_UNSPECIFIED;
+
+ uint16_t mtu_size = client->get_mtu(connection_handle);
+ offset = write_response.offset + write_response.partial_value.size();
+ if (offset < len) {
+ err = client->_pal_client->queue_prepare_write(
+ connection_handle, attribute_handle,
+ make_const_Span(
+ data + offset,
+ std::min((len - offset), (mtu_size - 5))
+ ),
+ offset
+ );
+ } else {
+ err = client->_pal_client->execute_write_queue(
+ connection_handle, true
+ );
+ }
+
+ if (err) {
+ clear_prepare_queue(client, err, AttErrorResponse::UNLIKELY_ERROR);
+ }
+ }
+
+ void handle_execute_write_response(GenericGattClient* client, const AttExecuteWriteResponse& execute_response) {
+ if (prepare_success) {
+ status = BLE_ERROR_NONE;
+ error_code = 0x00;
+ }
+
+ GattWriteCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_REQ,
+ status,
+ error_code
+ };
+
+ terminate(client, response);
+ }
+
+ void clear_prepare_queue(GenericGattClient* client, ble_error_t s, uint8_t e) {
+ prepare_success = false;
+ status = s;
+ error_code = e;
+ ble_error_t err = client->_pal_client->execute_write_queue(
+ connection_handle, false
+ );
+
+ if (err) {
+ GattWriteCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_REQ,
+ err,
+ AttErrorResponse::UNLIKELY_ERROR
+ };
+
+ terminate(client, response);
+ }
+ }
+
+ void handle_error(GenericGattClient* client, const AttErrorResponse& error) {
+ ble_error_t status = BLE_ERROR_UNSPECIFIED;
+
+ switch (error.error_code) {
+ case AttErrorResponse::INVALID_HANDLE:
+ status = BLE_ERROR_INVALID_PARAM;
+ break;
+ case AttErrorResponse::INVALID_ATTRIBUTE_VALUE_LENGTH:
+ status = BLE_ERROR_INVALID_PARAM;
+ break;
+ case AttErrorResponse::INSUFFICIENT_AUTHORIZATION:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::INSUFFICIENT_AUTHENTICATION:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::INSUFFICIENT_ENCRYPTION_KEY_SIZE:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::INSUFFICIENT_ENCRYPTION:
+ status = BLE_ERROR_INVALID_STATE;
+ break;
+ case AttErrorResponse::WRITE_NOT_PERMITTED:
+ status = BLE_ERROR_OPERATION_NOT_PERMITTED;
+ break;
+ default:
+ status = BLE_ERROR_UNSPECIFIED;
+ break;
+ }
+
+ if (error.request_opcode == AttributeOpcode(AttributeOpcode::PREPARE_WRITE_REQUEST)) {
+ clear_prepare_queue(client, status, error.error_code);
+ } else {
+ GattWriteCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_REQ,
+ status,
+ error.error_code
+ };
+
+ terminate(client, response);
+ }
+ }
+
+ uint16_t attribute_handle;
+ uint16_t len;
+ uint16_t offset;
+ uint8_t* data;
+ bool prepare_success;
+ ble_error_t status;
+ uint8_t error_code;
+};
+
+/*
+ * Control block for the descriptor discovery process
+ */
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+struct GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::DescriptorDiscoveryControlBlock : public ProcedureControlBlock {
+ using ProcedureControlBlock::connection_handle;
+
+ DescriptorDiscoveryControlBlock(
+ const DiscoveredCharacteristic& characteristic,
+ const CharacteristicDescriptorDiscovery::DiscoveryCallback_t& discoveryCallback,
+ const CharacteristicDescriptorDiscovery::TerminationCallback_t& terminationCallback
+ ) : ProcedureControlBlock(DESCRIPTOR_DISCOVERY_PROCEDURE, characteristic.getConnectionHandle()),
+ characteristic(characteristic),
+ discovery_cb(discoveryCallback),
+ termination_cb(terminationCallback),
+ next_handle(characteristic.getValueHandle() + 1),
+ done(false) {
+ }
+
+ virtual ~DescriptorDiscoveryControlBlock() { }
+
+ ble_error_t start(GenericGattClient* client) {
+ return client->_pal_client->discover_characteristics_descriptors(
+ connection_handle,
+ attribute_handle_range(
+ next_handle,
+ characteristic.getLastHandle()
+ )
+ );
+ }
+
+ virtual void handle_timeout_error(GenericGattClient* client) {
+ terminate(client, BLE_ERROR_UNSPECIFIED);
+ }
+
+ virtual void abort(GenericGattClient *client) {
+ terminate(client, BLE_ERROR_INVALID_STATE);
+ }
+
+ virtual void handle(GenericGattClient* client, const AttServerMessage& message) {
+ if (done) {
+ terminate(client, BLE_ERROR_NONE);
+ return;
+ }
+
+ switch(message.opcode) {
+ case AttributeOpcode::ERROR_RESPONSE:
+ handle_error(client, static_cast<const AttErrorResponse&>(message));
+ return;
+
+ case AttributeOpcode::FIND_INFORMATION_RESPONSE:
+ handle_response(client, static_cast<const AttFindInformationResponse&>(message));
+ return;
+
+ default:
+ break;
+ }
+ }
+
+ void handle_error(GenericGattClient* client, const AttErrorResponse& error) {
+ if (error.error_code == AttErrorResponse::ATTRIBUTE_NOT_FOUND) {
+ terminate(client, BLE_ERROR_NONE);
+ } else {
+ terminate(client, BLE_ERROR_UNSPECIFIED, error.error_code);
+ }
+ }
+
+ void handle_response(GenericGattClient* client, const AttFindInformationResponse& response) {
+ for (size_t i = 0; i < response.size(); ++i) {
+ DiscoveredCharacteristicDescriptor descriptor(
+ client, connection_handle, response[i].handle, response[i].uuid
+ );
+ CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t params = {
+ characteristic,
+ descriptor
+ };
+ discovery_cb(¶ms);
+ if (done) {
+ terminate(client, BLE_ERROR_NONE);
+ return;
+ }
+
+ if (response[i].handle == characteristic.getLastHandle()) {
+ terminate(client, BLE_ERROR_NONE);
+ return;
+ }
+ next_handle = response[i].handle + 1;
+ }
+
+ ble_error_t err = start(client);
+ if (err) {
+ terminate(client, err, AttErrorResponse::UNLIKELY_ERROR);
+ }
+ }
+
+ void terminate(GenericGattClient* client, ble_error_t status, uint8_t error_code = 0x00) {
+ client->remove_control_block(this);
+ CharacteristicDescriptorDiscovery::TerminationCallbackParams_t params = {
+ characteristic,
+ status,
+ error_code
+ };
+ termination_cb(¶ms);
+ delete this;
+ }
+
+ DiscoveredCharacteristic characteristic;
+ CharacteristicDescriptorDiscovery::DiscoveryCallback_t discovery_cb;
+ CharacteristicDescriptorDiscovery::TerminationCallback_t termination_cb;
+ uint16_t next_handle;
+ bool done;
+};
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::GenericGattClient(PalGattClient* pal_client) :
+ _pal_client(pal_client),
+ _termination_callback(),
+#if BLE_FEATURE_SIGNING
+ _signing_event_handler(NULL),
+#endif
+ control_blocks(NULL),
+ _is_reseting(false) {
+ _pal_client->when_server_message_received(
+ mbed::callback(this, &GenericGattClient::on_server_message_received)
+ );
+ _pal_client->when_transaction_timeout(
+ mbed::callback(this, &GenericGattClient::on_transaction_timeout)
+ );
+ _pal_client->set_event_handler(this);
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+ble_error_t GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::launchServiceDiscovery_(
+ connection_handle_t connection_handle,
+ ServiceDiscovery::ServiceCallback_t service_callback,
+ ServiceDiscovery::CharacteristicCallback_t characteristic_callback,
+ const UUID& matching_service_uuid,
+ const UUID& matching_characteristic_uuid
+) {
+ // verify that there is no other procedures going on this connection
+ if (_is_reseting || get_control_block(connection_handle)) {
+ return BLE_ERROR_INVALID_STATE;
+ }
+
+ // terminate and return if there is no callback to call
+ if (!service_callback && !characteristic_callback) {
+ on_termination(connection_handle);
+ return BLE_ERROR_NONE;
+ }
+
+ DiscoveryControlBlock* discovery_pcb = new(std::nothrow) DiscoveryControlBlock(
+ connection_handle,
+ service_callback,
+ characteristic_callback,
+ matching_service_uuid,
+ matching_characteristic_uuid
+ );
+
+ if (discovery_pcb == NULL) {
+ return BLE_ERROR_NO_MEM;
+ }
+
+ // note: control block inserted prior the request because they are part of
+ // of the transaction and the callback can be call synchronously
+ insert_control_block(discovery_pcb);
+
+ // launch the request
+ ble_error_t err = BLE_ERROR_UNSPECIFIED;
+ if (matching_service_uuid == UUID()) {
+ err = _pal_client->discover_primary_service(
+ connection_handle,
+ 0x0001
+ );
+ } else {
+ err = _pal_client->discover_primary_service_by_service_uuid(
+ connection_handle,
+ 0x0001,
+ matching_service_uuid
+ );
+ }
+
+ if (err) {
+ remove_control_block(discovery_pcb);
+ delete discovery_pcb;
+ }
+
+ return err;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+bool GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::isServiceDiscoveryActive_() const {
+ ProcedureControlBlock* pcb = control_blocks;
+
+ while (pcb) {
+ if (pcb->type == COMPLETE_DISCOVERY_PROCEDURE) {
+ return true;
+ }
+ pcb = pcb->next;
+ }
+
+ return false;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::terminateServiceDiscovery_()
+{
+ ProcedureControlBlock* pcb = control_blocks;
+ while (pcb) {
+ if (pcb->type == COMPLETE_DISCOVERY_PROCEDURE) {
+ static_cast<DiscoveryControlBlock*>(pcb)->done = true;
+ }
+ pcb = pcb->next;
+ }
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+ble_error_t GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::read_(
+ connection_handle_t connection_handle,
+ GattAttribute::Handle_t attribute_handle,
+ uint16_t offset) const
+{
+ // verify that there is no other procedures going on this connection
+ if (_is_reseting || get_control_block(connection_handle)) {
+ return BLE_ERROR_INVALID_STATE;
+ }
+
+ ReadControlBlock* read_pcb = new(std::nothrow) ReadControlBlock(
+ connection_handle,
+ attribute_handle,
+ offset
+ );
+
+ if (read_pcb == NULL) {
+ return BLE_ERROR_NO_MEM;
+ }
+
+ insert_control_block(read_pcb);
+
+ ble_error_t err = BLE_ERROR_NONE;
+
+ if (offset == 0) {
+ err = _pal_client->read_attribute_value(
+ connection_handle, attribute_handle
+ );
+ } else {
+ err = _pal_client->read_attribute_blob(
+ connection_handle, attribute_handle, offset
+ );
+ }
+
+ if (err) {
+ remove_control_block(read_pcb);
+ delete read_pcb;
+ }
+
+ return err;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+ble_error_t GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::write_(
+ WriteOp_t cmd,
+ connection_handle_t connection_handle,
+ GattAttribute::Handle_t attribute_handle,
+ size_t length,
+ const uint8_t* value
+) const {
+ // verify that there is no other procedures going on this connection
+ if (_is_reseting || get_control_block(connection_handle)) {
+ return BLE_ERROR_INVALID_STATE;
+ }
+
+ uint16_t mtu = get_mtu(connection_handle);
+
+#if BLE_FEATURE_SIGNING
+ /* if link is encrypted signed writes should be normal writes */
+ if (cmd == Base::GATT_OP_SIGNED_WRITE_CMD) {
+ ble::link_encryption_t encryption(ble::link_encryption_t::NOT_ENCRYPTED);
+ // FIXME: use security manager or a template if applicable
+ SecurityManager &sm = createBLEInstance()->getSecurityManager();
+ ble_error_t status = sm.getLinkEncryption(connection_handle, &encryption);
+ if (status == BLE_ERROR_NONE &&
+ (encryption == link_encryption_t::ENCRYPTED ||
+ encryption == link_encryption_t::ENCRYPTED_WITH_MITM ||
+ encryption == link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM)
+ ) {
+ cmd = Base::GATT_OP_WRITE_CMD;
+ }
+ }
+#endif // BLE_FEATURE_SIGNING
+
+ if (cmd == Base::GATT_OP_WRITE_CMD) {
+ if (length > (uint16_t) (mtu - WRITE_HEADER_LENGTH)) {
+ return BLE_ERROR_PARAM_OUT_OF_RANGE;
+ }
+ return _pal_client->write_without_response(
+ connection_handle,
+ attribute_handle,
+ make_const_Span(value, length)
+ );
+#if BLE_FEATURE_SIGNING
+ } else if (cmd == Base::GATT_OP_SIGNED_WRITE_CMD) {
+ if (length > (uint16_t) (mtu - WRITE_HEADER_LENGTH - CMAC_LENGTH - MAC_COUNTER_LENGTH)) {
+ return BLE_ERROR_PARAM_OUT_OF_RANGE;
+ }
+ ble_error_t status = _pal_client->signed_write_without_response(
+ connection_handle,
+ attribute_handle,
+ make_const_Span(value, length)
+ );
+
+ if (_signing_event_handler && (status == BLE_ERROR_NONE)) {
+ _signing_event_handler->on_signed_write();
+ }
+ return status;
+#endif // BLE_FEATURE_SIGNING
+ } else if (cmd == GattClient::GATT_OP_WRITE_REQ) {
+ uint8_t* data = NULL;
+
+ if (length > (uint16_t) (mtu - WRITE_HEADER_LENGTH)) {
+ data = (uint8_t*) malloc(length);
+ if (data == NULL) {
+ return BLE_ERROR_NO_MEM;
+ }
+ memcpy(data, value, length);
+ }
+
+ WriteControlBlock* write_pcb = new (std::nothrow) WriteControlBlock(
+ connection_handle,
+ attribute_handle,
+ data,
+ length
+ );
+
+ if (write_pcb == NULL) {
+ free(data);
+ return BLE_ERROR_NO_MEM;
+ }
+
+ insert_control_block(write_pcb);
+
+ ble_error_t err = BLE_ERROR_UNSPECIFIED;
+ if (data) {
+ err = _pal_client->queue_prepare_write(
+ connection_handle,
+ attribute_handle,
+ make_const_Span(value, mtu - PREPARE_WRITE_HEADER_LENGTH),
+ /* offset */0
+ );
+ } else {
+ err = _pal_client->write_attribute(
+ connection_handle,
+ attribute_handle,
+ make_const_Span(value, length)
+ );
+ }
+
+ if (err) {
+ remove_control_block(write_pcb);
+ delete write_pcb;
+ }
+
+ return err;
+ }
+
+ return BLE_ERROR_NOT_IMPLEMENTED;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::onServiceDiscoveryTermination_(
+ ServiceDiscovery::TerminationCallback_t callback
+) {
+ _termination_callback = callback;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+ble_error_t GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::discoverCharacteristicDescriptors_(
+ const DiscoveredCharacteristic& characteristic,
+ const CharacteristicDescriptorDiscovery::DiscoveryCallback_t& discoveryCallback,
+ const CharacteristicDescriptorDiscovery::TerminationCallback_t& terminationCallback
+) {
+ // verify that there is no other procedures going on this connection
+ if (_is_reseting || get_control_block(characteristic.getConnectionHandle())) {
+ return BLE_ERROR_INVALID_STATE;
+ }
+
+ if (characteristic.getValueHandle() == characteristic.getLastHandle()) {
+ CharacteristicDescriptorDiscovery::TerminationCallbackParams_t params = {
+ characteristic,
+ BLE_ERROR_NONE,
+ /* error code */ 0x00
+ };
+
+ terminationCallback(¶ms);
+ return BLE_ERROR_NONE;
+ }
+
+ DescriptorDiscoveryControlBlock* discovery_pcb =
+ new(std::nothrow) DescriptorDiscoveryControlBlock(
+ characteristic,
+ discoveryCallback,
+ terminationCallback
+ );
+
+ if (discovery_pcb == NULL) {
+ return BLE_ERROR_NO_MEM;
+ }
+
+ insert_control_block(discovery_pcb);
+
+ ble_error_t err = discovery_pcb->start(this);
+
+ if (err) {
+ remove_control_block(discovery_pcb);
+ delete discovery_pcb;
+ }
+
+ return err;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+bool GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::isCharacteristicDescriptorDiscoveryActive_(
+ const DiscoveredCharacteristic& characteristic
+) const {
+ ProcedureControlBlock* pcb = control_blocks;
+
+ while (pcb) {
+ if (pcb->type == DESCRIPTOR_DISCOVERY_PROCEDURE &&
+ static_cast<DescriptorDiscoveryControlBlock*>(pcb)->characteristic == characteristic) {
+ return true;
+ }
+ pcb = pcb->next;
+ }
+
+ return false;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::terminateCharacteristicDescriptorDiscovery_(
+ const DiscoveredCharacteristic& characteristic
+) {
+ ProcedureControlBlock* pcb = control_blocks;
+
+ while (pcb) {
+ if (pcb->type == DESCRIPTOR_DISCOVERY_PROCEDURE) {
+ DescriptorDiscoveryControlBlock* dpcb =
+ static_cast<DescriptorDiscoveryControlBlock*>(pcb);
+ if (dpcb->characteristic == characteristic) {
+ dpcb->done = true;
+ return;
+ }
+ }
+
+ pcb = pcb->next;
+ }
+
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+ble_error_t GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::negotiateAttMtu_(
+ connection_handle_t connection
+) {
+ return _pal_client->exchange_mtu(connection);
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+ble_error_t GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::reset_(void) {
+
+ Base::reset_();
+
+ // _is_reseting prevent executions of new procedure while the instance resets.
+ // otherwise new procedures can be launched from callbacks generated by the
+ // reset.
+ _is_reseting = true;
+ while (control_blocks) {
+ control_blocks->abort(this);
+ }
+ _is_reseting = false;
+
+ return BLE_ERROR_NONE;
+}
+
+#if BLE_FEATURE_SIGNING
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::set_signing_event_handler_(
+ SigningMonitorEventHandler *signing_event_handler
+) {
+ _signing_event_handler = signing_event_handler;
+}
+#endif // BLE_FEATURE_SIGNING
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::on_att_mtu_change_(
+ ble::connection_handle_t connection_handle,
+ uint16_t att_mtu_size
+)
+{
+ if (eventHandler) {
+ eventHandler->onAttMtuChange(connection_handle, att_mtu_size);
+ }
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::on_write_command_sent_(
+ ble::connection_handle_t connection_handle,
+ ble::attribute_handle_t attribute_handle,
+ uint8_t status
+) {
+ GattWriteCallbackParams response = {
+ connection_handle,
+ attribute_handle,
+ GattWriteCallbackParams::OP_WRITE_CMD,
+ BLE_ERROR_NONE,
+ status
+ };
+
+ this->processWriteResponse(&response);
+}
+
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::on_termination(connection_handle_t connection_handle) {
+ if (_termination_callback) {
+ _termination_callback(connection_handle);
+ }
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::on_server_message_received(
+ connection_handle_t connection_handle,
+ const AttServerMessage& message
+) {
+ switch(message.opcode) {
+ case AttributeOpcode::ERROR_RESPONSE:
+ case AttributeOpcode::EXCHANGE_MTU_RESPONSE:
+ case AttributeOpcode::FIND_INFORMATION_RESPONSE:
+ case AttributeOpcode::FIND_BY_VALUE_TYPE_RESPONSE:
+ case AttributeOpcode::READ_BY_TYPE_RESPONSE:
+ case AttributeOpcode::READ_RESPONSE:
+ case AttributeOpcode::READ_BLOB_RESPONSE:
+ case AttributeOpcode::READ_MULTIPLE_RESPONSE:
+ case AttributeOpcode::READ_BY_GROUP_TYPE_RESPONSE:
+ case AttributeOpcode::WRITE_RESPONSE:
+ case AttributeOpcode::PREPARE_WRITE_RESPONSE:
+ case AttributeOpcode::EXECUTE_WRITE_RESPONSE: {
+ on_server_response(connection_handle, message);
+ } break;
+
+ case AttributeOpcode::HANDLE_VALUE_INDICATION:
+ case AttributeOpcode::HANDLE_VALUE_NOTIFICATION: {
+ on_server_event(connection_handle, message);
+ } break;
+
+ default:
+ // invalid message receive
+ return;
+ }
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::on_server_response(
+ connection_handle_t connection,
+ const AttServerMessage& message
+) {
+ ProcedureControlBlock* pcb = get_control_block(connection);
+ if (pcb == NULL) {
+ return;
+ }
+
+ pcb->handle(this, message);
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::on_server_event(connection_handle_t connection, const AttServerMessage& message) {
+ GattHVXCallbackParams callbacks_params = {
+ (connection_handle_t) connection, 0
+ };
+
+ switch (message.opcode) {
+ case AttributeOpcode::HANDLE_VALUE_NOTIFICATION: {
+ const AttHandleValueNotification& notification =
+ static_cast<const AttHandleValueNotification&>(message);
+ callbacks_params.handle = notification.attribute_handle;
+ callbacks_params.type = BLE_HVX_NOTIFICATION;
+ callbacks_params.len = notification.attribute_value.size();
+ callbacks_params.data = notification.attribute_value.data();
+ } break;
+
+ case AttributeOpcode::HANDLE_VALUE_INDICATION: {
+ const AttHandleValueIndication& indication =
+ static_cast<const AttHandleValueIndication&>(message);
+ callbacks_params.handle = indication.attribute_handle;
+ callbacks_params.type = BLE_HVX_INDICATION;
+ callbacks_params.len = indication.attribute_value.size();
+ callbacks_params.data = indication.attribute_value.data();
+ } break;
+
+ default:
+ return;
+ }
+
+ Base::processHVXEvent(&callbacks_params);
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::on_transaction_timeout(connection_handle_t connection) {
+ ProcedureControlBlock* pcb = get_control_block(connection);
+ if (pcb == NULL) {
+ return;
+ }
+
+ pcb->handle_timeout_error(this);
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+typename GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::ProcedureControlBlock*
+GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::get_control_block(connection_handle_t connection) {
+ ProcedureControlBlock* it = control_blocks;
+ while (it && it->connection_handle != connection) {
+ it = it->next;
+ }
+ return it;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+const typename GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::ProcedureControlBlock*
+GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::get_control_block(connection_handle_t connection) const {
+ ProcedureControlBlock* it = control_blocks;
+ while (it && it->connection_handle != connection) {
+ it = it->next;
+ }
+ return it;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::insert_control_block(ProcedureControlBlock* cb) const {
+ if (control_blocks == NULL) {
+ control_blocks = cb;
+ return;
+ }
+
+ ProcedureControlBlock* current = control_blocks;
+ while (current->next) {
+ current = current->next;
+ }
+ current->next = cb;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+void GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::remove_control_block(ProcedureControlBlock* cb) const {
+ if (control_blocks == NULL) {
+ return;
+ }
+
+ if (cb == control_blocks) {
+ control_blocks = control_blocks->next;
+ return;
+ }
+
+ ProcedureControlBlock* current = control_blocks;
+ while (current->next && current->next != cb) {
+ current = current->next;
+ }
+
+ if (current->next == NULL) {
+ return;
+ }
+
+ current->next = cb->next;
+ cb->next = NULL;
+}
+
+template<template<class> class TPalGattClient, class SigningMonitorEventHandler>
+uint16_t GenericGattClient<TPalGattClient, SigningMonitorEventHandler>::get_mtu(connection_handle_t connection) const {
+ uint16_t result = 23;
+ if(_pal_client->get_mtu_size((connection_handle_t) connection, result) != BLE_ERROR_NONE) {
+ result = 23;
+ }
+ return result;
+}
+
+} // namespace pal
+} // namespace ble
+
+#endif // BLE_FEATURE_GATT_SERVER