Newer
Older
mbed-os / connectivity / FEATURE_BLE / source / cordio / include / ble / internal / PalGapImpl.h
@Vincent Coubard Vincent Coubard on 25 Aug 2020 17 KB BLE: Cleanup pal Gap
/* 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_GAP_
#define IMPL_PAL_GAP_

#include "source/pal/PalGap.h"
#include "dm_api.h"

namespace ble {
namespace impl {

/**
 * Implementation of ble::PalGap for the Cordio stack.
 */
class PalGap final : public ble::PalGap {
public:
    PalGap() : _pal_event_handler(nullptr), use_active_scanning(false)
    {
    };

    ~PalGap() = default;

    bool is_feature_supported(
        ble::controller_supported_features_t feature
    ) final;

    ble_error_t initialize() final;

    ble_error_t terminate() final;

    address_t get_device_address() final;

    address_t get_random_address() final;

    ble_error_t set_random_address(const address_t &address) final;

    ble_error_t set_advertising_parameters(
        uint16_t advertising_interval_min,
        uint16_t advertising_interval_max,
        advertising_type_t advertising_type,
        own_address_type_t own_address_type,
        advertising_peer_address_type_t peer_address_type,
        const address_t &peer_address,
        advertising_channel_map_t advertising_channel_map,
        advertising_filter_policy_t advertising_filter_policy
    ) final;

    ble_error_t set_advertising_data(
        uint8_t advertising_data_length,
        const advertising_data_t &advertising_data
    ) final;

    ble_error_t set_scan_response_data(
        uint8_t scan_response_data_length,
        const advertising_data_t &scan_response_data
    ) final;

    ble_error_t advertising_enable(bool enable) final;

    ble_error_t set_scan_parameters(
        bool active_scanning,
        uint16_t scan_interval,
        uint16_t scan_window,
        own_address_type_t own_address_type,
        scanning_filter_policy_t filter_policy
    ) final;

    ble_error_t scan_enable(
        bool enable,
        bool filter_duplicates
    ) final;

    ble_error_t create_connection(
        uint16_t scan_interval,
        uint16_t scan_window,
        initiator_policy_t initiator_policy,
        connection_peer_address_type_t peer_address_type,
        const address_t &peer_address,
        own_address_type_t own_address_type,
        uint16_t connection_interval_min,
        uint16_t connection_interval_max,
        uint16_t connection_latency,
        uint16_t supervision_timeout,
        uint16_t minimum_connection_event_length,
        uint16_t maximum_connection_event_length
    ) final;

    ble_error_t cancel_connection_creation();

    uint8_t read_white_list_capacity() final;

    ble_error_t clear_whitelist() final;

    ble_error_t add_device_to_whitelist(
        whitelist_address_type_t address_type,
        address_t address
    ) final;

    ble_error_t remove_device_from_whitelist(
        whitelist_address_type_t address_type,
        address_t address
    ) final;

    ble_error_t connection_parameters_update(
        connection_handle_t connection,
        uint16_t connection_interval_min,
        uint16_t connection_interval_max,
        uint16_t connection_latency,
        uint16_t supervision_timeout,
        uint16_t minimum_connection_event_length,
        uint16_t maximum_connection_event_length
    ) final;

    ble_error_t accept_connection_parameter_request(
        connection_handle_t connection_handle,
        uint16_t interval_min,
        uint16_t interval_max,
        uint16_t latency,
        uint16_t supervision_timeout,
        uint16_t minimum_connection_event_length,
        uint16_t maximum_connection_event_length
    ) final;

    ble_error_t reject_connection_parameter_request(
        connection_handle_t connection_handle,
        hci_error_code_t rejection_reason
    ) final;

    ble_error_t disconnect(
        connection_handle_t connection,
        local_disconnection_reason_t disconnection_reason
    ) final;

    bool is_privacy_supported() final;

    ble_error_t set_address_resolution(
        bool enable
    ) final;

    ble_error_t read_phy(connection_handle_t connection) final;

    ble_error_t set_preferred_phys(
        const phy_set_t &tx_phys,
        const phy_set_t &rx_phys
    ) final;

    ble_error_t set_phy(
        connection_handle_t connection,
        const phy_set_t &tx_phys,
        const phy_set_t &rx_phys,
        coded_symbol_per_bit_t coded_symbol
    ) final;

    // singleton of the ARM Cordio client
    static PalGap &get_gap();

    /**
     * Callback which handle wsfMsgHdr_t and forward them to emit_gap_event.
     */
    static void gap_handler(const wsfMsgHdr_t *msg);

    ble_error_t set_advertising_set_random_address(
        advertising_handle_t advertising_handle,
        const address_t &address
    ) final;

    ble_error_t set_extended_advertising_parameters(
        advertising_handle_t advertising_handle,
        advertising_event_properties_t event_properties,
        advertising_interval_t primary_advertising_interval_min,
        advertising_interval_t primary_advertising_interval_max,
        advertising_channel_map_t primary_advertising_channel_map,
        own_address_type_t own_address_type,
        advertising_peer_address_type_t peer_address_type,
        const address_t &peer_address,
        advertising_filter_policy_t advertising_filter_policy,
        advertising_power_t advertising_power,
        phy_t primary_advertising_phy,
        uint8_t secondary_advertising_max_skip,
        phy_t secondary_phy,
        uint8_t advertising_sid,
        bool scan_request_notification
    ) final;

    ble_error_t set_periodic_advertising_parameters(
        advertising_handle_t advertising_handle,
        periodic_advertising_interval_t periodic_advertising_min,
        periodic_advertising_interval_t periodic_advertising_max,
        bool advertise_power
    ) final;

    ble_error_t set_extended_advertising_data(
        advertising_handle_t advertising_handle,
        advertising_fragment_description_t operation,
        bool minimize_fragmentation,
        uint8_t advertising_data_size,
        const uint8_t *advertising_data
    ) final;

    ble_error_t set_periodic_advertising_data(
        advertising_handle_t advertising_handle,
        advertising_fragment_description_t fragment_description,
        uint8_t advertising_data_size,
        const uint8_t *advertising_data
    ) final;

    ble_error_t set_extended_scan_response_data(
        advertising_handle_t advertising_handle,
        advertising_fragment_description_t operation,
        bool minimize_fragmentation,
        uint8_t scan_response_data_size,
        const uint8_t *scan_response_data
    ) final;

    ble_error_t extended_advertising_enable(
        bool enable,
        uint8_t number_of_sets,
        const advertising_handle_t *handles,
        const uint16_t *durations,
        const uint8_t *max_extended_advertising_events
    ) final;

    ble_error_t periodic_advertising_enable(
        bool enable,
        advertising_handle_t advertising_handle
    ) final;

    uint16_t get_maximum_advertising_data_length() final;

    uint16_t get_maximum_connectable_advertising_data_length() final;

    uint8_t get_maximum_hci_advertising_data_length() final;

    uint8_t get_max_number_of_advertising_sets() final;

    ble_error_t remove_advertising_set(
        advertising_handle_t advertising_handle
    ) final;

    ble_error_t clear_advertising_sets() final;

    ble_error_t set_extended_scan_parameters(
        own_address_type_t own_address_type,
        scanning_filter_policy_t filter_policy,
        phy_set_t scanning_phys,
        const bool *active_scanning,
        const uint16_t *scan_interval,
        const uint16_t *scan_window
    ) final;

    ble_error_t extended_scan_enable(
        bool enable,
        duplicates_filter_t filter_duplicates,
        uint16_t duration,
        uint16_t period
    ) final;

    ble_error_t periodic_advertising_create_sync(
        bool use_periodic_advertiser_list,
        uint8_t advertising_sid,
        peer_address_type_t peer_address_type,
        const address_t &peer_address,
        uint16_t allowed_skip,
        uint16_t sync_timeout
    ) final;

    ble_error_t cancel_periodic_advertising_create_sync() final;

    ble_error_t periodic_advertising_terminate_sync(
        sync_handle_t sync_handle
    ) final;

    ble_error_t add_device_to_periodic_advertiser_list(
        advertising_peer_address_type_t advertiser_address_type,
        const address_t &advertiser_address,
        uint8_t advertising_sid
    ) final;

    ble_error_t remove_device_from_periodic_advertiser_list(
        advertising_peer_address_type_t advertiser_address_type,
        const address_t &advertiser_address,
        uint8_t advertising_sid
    ) final;

    ble_error_t clear_periodic_advertiser_list() final;

    uint8_t read_periodic_advertiser_list_size() final;

    ble_error_t extended_create_connection(
        initiator_policy_t initiator_policy,
        own_address_type_t own_address_type,
        peer_address_type_t peer_address_type,
        const address_t &peer_address,
        phy_set_t initiating_phys,
        const uint16_t *scan_intervals,
        const uint16_t *scan_windows,
        const uint16_t *connection_intervals_min,
        const uint16_t *connection_intervals_max,
        const uint16_t *connection_latencies,
        const uint16_t *supervision_timeouts,
        const uint16_t *minimum_connection_event_lengths,
        const uint16_t *maximum_connection_event_lengths
    ) final;

    void when_gap_event_received(mbed::Callback<void(const GapEvent &)> cb) final;

    /**
    * Sets the event handler that us called by the PAL porters to notify the stack of events
    * which will in turn be passed onto the user application when appropriate.
    *
    * @param[in] event_handler the new event handler interface implementation. Memory
    * owned by caller who is responsible for updating this pointer if interface changes.
    */
    void set_event_handler(PalGapEventHandler *event_handler) final;

    PalGapEventHandler *get_event_handler() final;

private:
    /**
     * Called this function whenever the LE subsystem
     * generate a PalGap event.
     *
     * @param gap_event The event to emit to higher layer.
     */
    void emit_gap_event(const GapEvent &gap_event)
    {
        if (_gap_event_cb) {
            _gap_event_cb(gap_event);
        }
    }

    /**
     * T shall define a can_convert and convert function and a type
     */
    template<typename T>
    static bool event_handler(const wsfMsgHdr_t *msg);

    /**
     * Traits defining can_convert for events.
     */
    template<uint8_t EventID>
    struct MessageConverter {
        static bool can_convert(const wsfMsgHdr_t *msg)
        {
            if (msg->event == EventID) {
                return true;
            }
            return false;
        }
    };

    struct ConnectionCompleteMessageConverter final : public MessageConverter<DM_CONN_OPEN_IND> {
        typedef hciLeConnCmplEvt_t type;

        static GapConnectionCompleteEvent convert(const hciLeConnCmplEvt_t *conn_evt)
        {
            return GapConnectionCompleteEvent(
                conn_evt->status,
                // note the usage of the stack handle, not the HCI handle
                conn_evt->hdr.param,
                (connection_role_t::type) conn_evt->role,
                (peer_address_type_t::type) conn_evt->addrType,
                conn_evt->peerAddr,
                conn_evt->connInterval,
                conn_evt->connLatency,
                conn_evt->supTimeout,
                conn_evt->localRpa,
                conn_evt->peerRpa
            );
        }
    };

    struct GapAdvertisingReportMessageConverter final : public MessageConverter<DM_SCAN_REPORT_IND> {
        typedef hciLeAdvReportEvt_t type;

        struct CordioGapAdvertisingReportEvent final : public GapAdvertisingReportEvent {
            CordioGapAdvertisingReportEvent(const advertising_t &advertising) :
                GapAdvertisingReportEvent(), advertising(advertising)
            {
            }

            uint8_t size() const final
            {
                return 1;
            }

            advertising_t operator[](uint8_t i) const final
            {
                return advertising;
            }

            advertising_t advertising;
        };

        static CordioGapAdvertisingReportEvent convert(const hciLeAdvReportEvt_t *scan_report)
        {
            GapAdvertisingReportEvent::advertising_t advertising = {
                (received_advertising_type_t::type) scan_report->eventType,
                (connection_peer_address_type_t::type) scan_report->addrType,
                scan_report->addr,
                make_const_Span(scan_report->pData, scan_report->len),
                scan_report->rssi
            };
            return CordioGapAdvertisingReportEvent(advertising);
        }
    };

    struct DisconnectionMessageConverter final : public MessageConverter<DM_CONN_CLOSE_IND> {
        typedef hciDisconnectCmplEvt_t type;

        static GapDisconnectionCompleteEvent convert(const hciDisconnectCmplEvt_t *disc_evt)
        {
            return GapDisconnectionCompleteEvent(
                disc_evt->status,
                // note the usage of the stack handle, not the HCI handle
                disc_evt->hdr.param,
                disc_evt->reason
            );
        }
    };

    struct ConnectionUpdateMessageConverter final : public MessageConverter<DM_CONN_UPDATE_IND> {
        typedef hciLeConnUpdateCmplEvt_t type;

        static GapConnectionUpdateEvent convert(const hciLeConnUpdateCmplEvt_t *evt)
        {
            return GapConnectionUpdateEvent(
                evt->status,
                evt->hdr.param,
                evt->connInterval,
                evt->connLatency,
                evt->supTimeout
            );
        }
    };

    struct RemoteConnectionParameterRequestMessageConverter final : public MessageConverter<DM_REM_CONN_PARAM_REQ_IND> {
        typedef hciLeRemConnParamReqEvt_t type;

        static GapRemoteConnectionParameterRequestEvent convert(const hciLeRemConnParamReqEvt_t *evt)
        {
            return GapRemoteConnectionParameterRequestEvent(
                evt->hdr.param,
                evt->intervalMin,
                evt->intervalMax,
                evt->latency,
                evt->timeout
            );
        }
    };

    /* Cordio requires the call to DmConnAccept to properly catch direct advertising timeout.
     * This call returns a connection ID and expect the peer address and the peer address type.
     * This PAL following HCI expectations, these information are not present when the higher
     * level calls the function to enable advertising.
     * This data structure caches these informations and use them in case of direct advertising.
     * Similarly, direct advertising timeout doesn't trigger an advertising timeout instead it
     * triggers a disconnection with the status 60. Information on running direct advertising
     * is stored here.
     */
    struct direct_adv_cb_t {
        direct_adv_cb_t() :
            peer_address_type(advertising_peer_address_type_t::PUBLIC),
            state(free)
        {
        }

        address_t peer_address;
        advertising_peer_address_type_t peer_address_type;
        advertising_handle_t advertising_handle;
        connection_handle_t connection_handle;
        bool low_duty_cycle;
        enum {
            free = 0,
            pending,
            running
        } state;
    };

    /* Predicate expect a const reference to a direct_adv_cb_t
     * It must returns true if the criteria are met and false otherwise. */
    template<typename Predicate>
    direct_adv_cb_t *get_adv_cb(const Predicate &predicate);

    direct_adv_cb_t *get_running_direct_adv_cb(advertising_handle_t adv_handle);

    direct_adv_cb_t *get_running_conn_direct_adv_cb(connection_handle_t adv_handle);

    direct_adv_cb_t *get_pending_direct_adv_cb(advertising_handle_t adv_handle);

    direct_adv_cb_t *get_free_direct_adv_cb();

    ble_error_t update_direct_advertising_parameters(
        advertising_handle_t advertising_handle,
        uint8_t advertising_type,
        address_t peer_address,
        advertising_peer_address_type_t peer_address_type
    );

    /**
     * Create an ALL_PHYS parameter used in LE Set PHY Command
     * and LE Set Default PHY Command.
     * @see BLUETOOTH SPECIFICATION Version 5.0 | Vol 2, Part E - 7.8.49
     */
    static uint8_t create_all_phys_value(
        const phy_set_t &tx_phys,
        const phy_set_t &rx_phys
    )
    {
        /* if phy set is empty set corresponding all_phys bit to 1 */
        uint8_t all_phys = 0;
        if (tx_phys.value() == 0) {
            all_phys |= 0x01;
        }
        if (rx_phys.value() == 0) {
            all_phys |= 0x02;
        }
        return all_phys;
    }

private:
    PalGapEventHandler *_pal_event_handler;
    address_t device_random_address;
    bool use_active_scanning;
    uint8_t extended_scan_type[3];
    phy_set_t scanning_phys;
    direct_adv_cb_t direct_adv_cb[DM_NUM_ADV_SETS];

    /**
     * Callback called when an event is emitted by the LE subsystem.
     */
    mbed::Callback<void(const GapEvent &)> _gap_event_cb;
};

} // namespace impl
} // ble

#endif /* IMPL_PAL_GAP_ */