/* mbed Microcontroller Library * Copyright (c) 2006-2020 ARM Limited * * SPDX-License-Identifier: Apache-2.0 * * 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 "source/pal/PalAttClient.h" #include "source/pal/PalSimpleAttServerMessage.h" #include "source/pal/PalGattClient.h" #include "source/GattServerImpl.h" #include "source/BLEInstanceBaseImpl.h" #include "source/PalAttClientImpl.h" #include "att_api.h" #include "att_defs.h" namespace ble { namespace impl { PalAttClient::PalAttClient() : _local_sign_counter(0) { } PalAttClient::~PalAttClient() = default; /** * @see ble::PalAttClient::exchange_mtu_request */ ble_error_t PalAttClient::exchange_mtu_request(connection_handle_t connection) { AttcMtuReq(connection, pAttCfg->mtu); return BLE_ERROR_NONE; } /** * @see ble::PalGattClient::get_mtu_size */ ble_error_t PalAttClient::get_mtu_size( connection_handle_t connection_handle, uint16_t &mtu_size ) { mtu_size = AttGetMtu(connection_handle); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::find_information_request */ ble_error_t PalAttClient::find_information_request( connection_handle_t connection_handle, attribute_handle_range_t discovery_range ) { AttcFindInfoReq( connection_handle, discovery_range.begin, discovery_range.end, false ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::find_by_type_value_request */ ble_error_t PalAttClient::find_by_type_value_request( connection_handle_t connection_handle, attribute_handle_range_t discovery_range, uint16_t type, const Span<const uint8_t> &value ) { AttcFindByTypeValueReq( connection_handle, discovery_range.begin, discovery_range.end, type, value.size(), const_cast<uint8_t *>(value.data()), false ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::read_by_type_request */ ble_error_t PalAttClient::read_by_type_request( connection_handle_t connection_handle, attribute_handle_range_t read_range, const UUID &type ) { AttcReadByTypeReq( connection_handle, read_range.begin, read_range.end, type.getLen(), const_cast<uint8_t *>(type.getBaseUUID()), false ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::read_request */ ble_error_t PalAttClient::read_request( connection_handle_t connection_handle, attribute_handle_t attribute_handle ) { AttcReadReq(connection_handle, attribute_handle); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::read_blob_request */ ble_error_t PalAttClient::read_blob_request( connection_handle_t connection_handle, attribute_handle_t attribute_handle, uint16_t offset ) { AttcReadLongReq( connection_handle, attribute_handle, offset, false ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::read_multiple_request */ ble_error_t PalAttClient::read_multiple_request( connection_handle_t connection_handle, const Span<const attribute_handle_t> &attribute_handles ) { AttcReadMultipleReq( connection_handle, attribute_handles.size(), const_cast<uint16_t *>(attribute_handles.data()) ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::read_by_group_type_request */ ble_error_t PalAttClient::read_by_group_type_request( connection_handle_t connection_handle, attribute_handle_range_t read_range, const UUID &group_type ) { AttcReadByGroupTypeReq( connection_handle, read_range.begin, read_range.end, group_type.getLen(), const_cast<uint8_t *>(group_type.getBaseUUID()), false ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::write_request */ ble_error_t PalAttClient::write_request( connection_handle_t connection_handle, attribute_handle_t attribute_handle, const Span<const uint8_t> &value ) { AttcWriteReq( connection_handle, attribute_handle, value.size(), const_cast<uint8_t *>(value.data()) ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::write_command */ ble_error_t PalAttClient::write_command( connection_handle_t connection_handle, attribute_handle_t attribute_handle, const Span<const uint8_t> &value ) { AttcWriteCmd( connection_handle, attribute_handle, value.size(), const_cast<uint8_t *>(value.data()) ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::signed_write_command */ ble_error_t PalAttClient::signed_write_command( connection_handle_t connection_handle, attribute_handle_t attribute_handle, const Span<const uint8_t> &value ) { AttcSignedWriteCmd( connection_handle, attribute_handle, _local_sign_counter, value.size(), const_cast<uint8_t *>(value.data()) ); _local_sign_counter++; return BLE_ERROR_NONE; } /** * Initialises the counter used to sign messages. Counter will be incremented every * time a message is signed. * * @param sign_counter initialise the signing counter to this value */ void PalAttClient::set_sign_counter( sign_count_t sign_counter ) { _local_sign_counter = sign_counter; } /** * @see ble::PalAttClient::prepare_write_request */ ble_error_t PalAttClient::prepare_write_request( connection_handle_t connection_handle, attribute_handle_t attribute_handle, uint16_t offset, const Span<const uint8_t> &value ) { AttcPrepareWriteReq( connection_handle, attribute_handle, offset, value.size(), const_cast<uint8_t *>(value.data()), false, false ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::execute_write_request */ ble_error_t PalAttClient::execute_write_request( connection_handle_t connection_handle, bool execute ) { AttcExecuteWriteReq( connection_handle, execute ); return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::initialize */ ble_error_t PalAttClient::initialize() { return BLE_ERROR_NONE; } /** * @see ble::PalAttClient::terminate */ ble_error_t PalAttClient::terminate() { return BLE_ERROR_NONE; } // singleton of the ARM Cordio client PalAttClient &PalAttClient::get_client() { static PalAttClient _client; return _client; } void PalAttClient::when_server_message_received( mbed::Callback<void(connection_handle_t, const AttServerMessage &)> cb ) { _server_message_cb = cb; } void PalAttClient::when_transaction_timeout( mbed::Callback<void(connection_handle_t)> cb ) { _transaction_timeout_cb = cb; } // convert an array of byte to an uint16_t uint16_t PalAttClient::to_uint16_t(const uint8_t *array) { uint16_t result; memcpy(&result, array, sizeof(result)); return result; } template<typename T> bool PalAttClient::event_handler(const attEvt_t *event) { if (T::can_convert(event)) { generated_handler(event, T::convert); return true; } return false; } bool PalAttClient::timeout_event_handler(const attEvt_t *event) { if (event->hdr.status != ATT_ERR_TIMEOUT) { return false; } get_client().on_transaction_timeout(event->hdr.param); return true; } template<typename ResultType> void PalAttClient::generated_handler( const attEvt_t *event, ResultType (*convert)(const attEvt_t *) ) { get_client().on_server_event( event->hdr.param, convert(event) ); } void PalAttClient::on_server_event( connection_handle_t connection_handle, const AttServerMessage &server_message ) { if (_server_message_cb) { _server_message_cb(connection_handle, server_message); } } /** * Upon transaction timeout an implementation shall call this function. * * @param connection_handle The handle of the connection of the transaction * which has times out. * * @note see BLUETOOTH SPECIFICATION Version 5.0 | Vol 3, Part F Section 3.3.3 */ void PalAttClient::on_transaction_timeout( connection_handle_t connection_handle ) { if (_transaction_timeout_cb) { _transaction_timeout_cb(connection_handle); } } void PalAttClient::att_client_handler(const attEvt_t *event) { #if BLE_FEATURE_GATT_CLIENT if (event->hdr.status == ATT_SUCCESS && event->hdr.event == ATT_MTU_UPDATE_IND) { ble::impl::BLEInstanceBase &ble = BLEInstanceBase::deviceInstance(); PalGattClientEventHandler *handler = ble.getPalGattClient().get_event_handler(); if (handler) { handler->on_att_mtu_change(event->hdr.param, event->mtu); } } else if (event->hdr.event == ATTC_WRITE_CMD_RSP) { ble::impl::BLEInstanceBase &ble = BLEInstanceBase::deviceInstance(); PalGattClientEventHandler *handler = ble.getPalGattClient().get_event_handler(); if (handler) { handler->on_write_command_sent( event->hdr.param, event->handle, event->hdr.status ); } } else { // all handlers are stored in a static array static const event_handler_t handlers[] = { &timeout_event_handler, &event_handler<ErrorResponseConverter>, &event_handler<FindInformationResponseConverter>, &event_handler<FindByTypeValueResponseConverter>, &event_handler<ReadByTypeResponseConverter>, &event_handler<ReadResponseConverter>, &event_handler<ReadBlobResponseConverter>, &event_handler<ReadMultipleResponseConverter>, &event_handler<ReadBygroupTypeResponseConverter>, &event_handler<WriteResponseConverter>, &event_handler<PrepareWriteResponseConverter>, &event_handler<ExecuteWriteResponseConverter>, &event_handler<HandleValueIndicationConverter>, &event_handler<HandleValueNotificationConverter> }; // event->hdr.param: connection handle // event->header.event: opcode from the request // event->header.status: success or error code ... // event->pValue: starting after opcode for response; starting after opcode + handle for server initiated responses. // event->handle: handle for server initiated responses // traverse all handlers and execute them with the event in input. // exit if an handler has handled the event. for (auto handler : handlers) { if (handler(event)) { return; } } } #endif // BLE_FEATURE_GATT_CLIENT #if BLE_FEATURE_GATT_SERVER // pass events not handled to the server side ble::impl::GattServer::att_cb(event); #endif // BLE_FEATURE_GATT_SERVER } } // namespace impl } // ble