Newer
Older
mbed-os / connectivity / FEATURE_BLE / source / cordio / source / PalAttClientImpl.h
/* 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.
 */

#ifndef IMPL_PAL_ATT_CLIENT_
#define IMPL_PAL_ATT_CLIENT_

#include "source/pal/PalSimpleAttServerMessage.h"
#include "source/pal/PalAttClient.h"

#include "att_api.h"

namespace ble {
namespace impl {

class PalAttClient final : public ble::PalAttClient {

public:
    PalAttClient();

    ~PalAttClient();

    /**
     * @see ble::PalAttClient::exchange_mtu_request
     */
    ble_error_t exchange_mtu_request(connection_handle_t connection) final;

    /**
     * @see ble::PalGattClient::get_mtu_size
     */
    ble_error_t get_mtu_size(
        connection_handle_t connection_handle,
        uint16_t &mtu_size
    ) final;

    /**
     * @see ble::PalAttClient::find_information_request
     */
    ble_error_t find_information_request(
        connection_handle_t connection_handle,
        attribute_handle_range_t discovery_range
    ) final;

    /**
     * @see ble::PalAttClient::find_by_type_value_request
     */
    ble_error_t 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
    ) final;

    /**
     * @see ble::PalAttClient::read_by_type_request
     */
    ble_error_t read_by_type_request(
        connection_handle_t connection_handle,
        attribute_handle_range_t read_range,
        const UUID &type
    ) final;

    /**
     * @see ble::PalAttClient::read_request
     */
    ble_error_t read_request(
        connection_handle_t connection_handle,
        attribute_handle_t attribute_handle
    ) final;

    /**
     * @see ble::PalAttClient::read_blob_request
     */
    ble_error_t read_blob_request(
        connection_handle_t connection_handle,
        attribute_handle_t attribute_handle,
        uint16_t offset
    ) final;

    /**
     * @see ble::PalAttClient::read_multiple_request
     */
    ble_error_t read_multiple_request(
        connection_handle_t connection_handle,
        const Span<const attribute_handle_t> &attribute_handles
    ) final;

    /**
     * @see ble::PalAttClient::read_by_group_type_request
     */
    ble_error_t read_by_group_type_request(
        connection_handle_t connection_handle,
        attribute_handle_range_t read_range,
        const UUID &group_type
    ) final;

    /**
     * @see ble::PalAttClient::write_request
     */
    ble_error_t write_request(
        connection_handle_t connection_handle,
        attribute_handle_t attribute_handle,
        const Span<const uint8_t> &value
    ) final;

    /**
     * @see ble::PalAttClient::write_command
     */
    ble_error_t write_command(
        connection_handle_t connection_handle,
        attribute_handle_t attribute_handle,
        const Span<const uint8_t> &value
    ) final;

    /**
     * @see ble::PalAttClient::signed_write_command
     */
    ble_error_t signed_write_command(
        connection_handle_t connection_handle,
        attribute_handle_t attribute_handle,
        const Span<const uint8_t> &value
    ) final;

    /**
     * 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 set_sign_counter(
        sign_count_t sign_counter
    );

    /**
     * @see ble::PalAttClient::prepare_write_request
     */
    ble_error_t prepare_write_request(
        connection_handle_t connection_handle,
        attribute_handle_t attribute_handle,
        uint16_t offset,
        const Span<const uint8_t> &value
    ) final;

    /**
     * @see ble::PalAttClient::execute_write_request
     */
    ble_error_t execute_write_request(
        connection_handle_t connection_handle,
        bool execute
    ) final;

    /**
     * @see ble::PalAttClient::initialize
     */
    ble_error_t initialize() final;

    /**
     * @see ble::PalAttClient::terminate
     */
    ble_error_t terminate() final;

    // singleton of the ARM Cordio client
    static PalAttClient &get_client();

    void when_server_message_received(
        mbed::Callback<void(connection_handle_t, const AttServerMessage &)> cb
    ) final;

    void when_transaction_timeout(
        mbed::Callback<void(connection_handle_t)> cb
    ) final;

private:
    // convert an array of byte to an uint16_t
    static uint16_t to_uint16_t(const uint8_t *array);

    /**
     * Type of an event handler.
     * @param The event to handle
     * @return true if the event has been handled and false otherwise.
     */
    typedef bool (*event_handler_t)(const attEvt_t *);

public:
    /**
     * Callback which handle attEvt_t and forward them to on_server_event.
     */
    static void att_client_handler(const attEvt_t *event);

private:
    /**
     * Event handler generator.
     * @tparam Description of an event converter. It contains two static member
     * function:
     *   - bool can_convert(const attEvt_t* event): return true if the event can
     *   be converted by the converter
     *   - <undefined> convert(const attEvt_t* event): return the
     *   AttServerMessage converted from event.
     * @param event
     * @return
     */
    template<typename T>
    static bool event_handler(const attEvt_t *event);

    static bool timeout_event_handler(const attEvt_t *event);

    template<typename ResultType>
    static void generated_handler(
        const attEvt_t *event, ResultType (*convert)(const attEvt_t *)
    );

    /**
     * Traits defining can_convert for non ErrorResponse events.
     */
    template<uint8_t RequestID>
    struct ResponseConverter {
        static bool can_convert(const attEvt_t *event)
        {
            if (event->hdr.status == ATT_SUCCESS && event->hdr.event == RequestID) {
                return true;
            }
            return false;
        }
    };

    /**
     * Converter for an AttErrorResponse.
     */
    struct ErrorResponseConverter {
        static bool can_convert(const attEvt_t *event)
        {
            if (event->hdr.status != ATT_SUCCESS) {
                return true;
            }
            return false;
        }

        static AttErrorResponse convert(const attEvt_t *event)
        {
            return AttErrorResponse(
                static_cast<AttributeOpcode::Code>(event->hdr.event * 2),
                event->handle,
                event->hdr.status
            );
        }
    };

    /**
     * Converter for a PalSimpleAttFindInformationResponse.
     */
    struct FindInformationResponseConverter : ResponseConverter<ATTC_FIND_INFO_RSP> {
        static PalSimpleAttFindInformationResponse convert(const attEvt_t *event)
        {
            return PalSimpleAttFindInformationResponse(
                static_cast<PalSimpleAttFindInformationResponse::Format>(event->pValue[0]),
                make_const_Span(
                    event->pValue + 1,
                    event->valueLen - 1
                )
            );
        }
    };

    /**
     * Converter for a PalSimpleAttFindByTypeValueResponse.
     */
    struct FindByTypeValueResponseConverter : ResponseConverter<ATTC_FIND_BY_TYPE_VALUE_RSP> {
        static PalSimpleAttFindByTypeValueResponse convert(const attEvt_t *event)
        {
            return PalSimpleAttFindByTypeValueResponse(
                make_const_Span(
                    event->pValue,
                    event->valueLen
                )
            );
        }
    };

    /**
     * Converter for a PalSimpleAttReadByTypeResponse.
     */
    struct ReadByTypeResponseConverter : ResponseConverter<ATTC_READ_BY_TYPE_RSP> {
        static PalSimpleAttReadByTypeResponse convert(const attEvt_t *event)
        {
            return PalSimpleAttReadByTypeResponse(
                event->pValue[0],
                make_const_Span(
                    event->pValue + 1,
                    event->valueLen - 1
                )
            );
        }
    };

    /**
     * Converter for a AttReadResponse.
     */
    struct ReadResponseConverter : ResponseConverter<ATTC_READ_RSP> {
        static AttReadResponse convert(const attEvt_t *event)
        {
            return AttReadResponse(
                make_const_Span(
                    event->pValue,
                    event->valueLen
                )
            );
        }
    };

    /**
     * Converter for a AttReadBlobResponse.
     */
    struct ReadBlobResponseConverter : ResponseConverter<ATTC_READ_LONG_RSP> {
        static AttReadBlobResponse convert(const attEvt_t *event)
        {
            return AttReadBlobResponse(
                make_const_Span(
                    event->pValue,
                    event->valueLen
                )
            );
        }
    };

    /**
     * Converter for a AttReadMultipleResponse.
     */
    struct ReadMultipleResponseConverter : ResponseConverter<ATTC_READ_MULTIPLE_RSP> {
        static AttReadMultipleResponse convert(const attEvt_t *event)
        {
            return AttReadMultipleResponse(
                make_const_Span(
                    event->pValue,
                    event->valueLen
                )
            );
        }
    };

    /**
     * Converter for a PalSimpleAttReadByGroupTypeResponse.
     */
    struct ReadBygroupTypeResponseConverter : ResponseConverter<ATTC_READ_BY_GROUP_TYPE_RSP> {
        static PalSimpleAttReadByGroupTypeResponse convert(const attEvt_t *event)
        {
            return PalSimpleAttReadByGroupTypeResponse(
                event->pValue[0],
                make_const_Span(
                    event->pValue + 1,
                    event->valueLen - 1
                )
            );
        }
    };

    /**
     * Converter for a AttWriteResponse.
     */
    struct WriteResponseConverter : ResponseConverter<ATTC_WRITE_RSP> {
        static AttWriteResponse convert(const attEvt_t *event)
        {
            return AttWriteResponse();
        }
    };

    /**
     * Converter for a AttPrepareWriteResponse.
     */
    struct PrepareWriteResponseConverter : ResponseConverter<ATTC_PREPARE_WRITE_RSP> {
        static AttPrepareWriteResponse convert(const attEvt_t *event)
        {
            return AttPrepareWriteResponse(
                event->handle,
                0, /* offset is lost */
                make_const_Span(
                    event->pValue,
                    event->valueLen
                )
            );
        }
    };

    /**
     * Converter for a AttExecuteWriteResponse.
     */
    struct ExecuteWriteResponseConverter : ResponseConverter<ATTC_EXECUTE_WRITE_RSP> {
        static AttExecuteWriteResponse convert(const attEvt_t *event)
        {
            return AttExecuteWriteResponse();
        }
    };

    /**
     * Converter for a AttHandleValueNotification.
     */
    struct HandleValueNotificationConverter : ResponseConverter<ATTC_HANDLE_VALUE_NTF> {
        static AttHandleValueNotification convert(const attEvt_t *event)
        {
            return AttHandleValueNotification(
                event->handle,
                make_const_Span(
                    event->pValue,
                    event->valueLen
                )
            );
        }
    };

    /**
     * Converter for a AttHandleValueIndication.
     */
    struct HandleValueIndicationConverter : ResponseConverter<ATTC_HANDLE_VALUE_IND> {
        static AttHandleValueIndication convert(const attEvt_t *event)
        {
            return AttHandleValueIndication(
                event->handle,
                make_const_Span(
                    event->pValue,
                    event->valueLen
                )
            );
        }
    };

private:
    /**
     * Upon server message reception an implementation shall call this function.
     *
     * @param connection_handle The handle of the connection which has received
     * the server message.
     * @param server_message The message received from the server.
     */
    void on_server_event(
        connection_handle_t connection_handle,
        const AttServerMessage &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 on_transaction_timeout(
        connection_handle_t connection_handle
    );

private:
    sign_count_t _local_sign_counter;

    /**
     * Callback called when the client receive a message from the server.
     */
    mbed::Callback<void(connection_handle_t, const AttServerMessage &)> _server_message_cb;

    /**
     * Callback called when a transaction times out.
     */
    mbed::Callback<void(connection_handle_t)> _transaction_timeout_cb;
};

} // namespace impl
} // namespace ble

#endif /* IMPL_PAL_ATT_CLIENT_ */