diff --git a/features/FEATURE_BLE/ble/Gap.h b/features/FEATURE_BLE/ble/Gap.h index d706d52bdd..044f4193ae 100644 --- a/features/FEATURE_BLE/ble/Gap.h +++ b/features/FEATURE_BLE/ble/Gap.h @@ -1017,6 +1017,7 @@ public: typedef uint8_t AdvHandle_t; static const AdvHandle_t LEGACY_ADVERTISING_HANDLE = 0x00; + static const AdvHandle_t INVALID_ADVERTISING_HANDLE = 0xFF; struct AdvReportOptionalInformation { PeerAddressType_t directAddressType; diff --git a/features/FEATURE_BLE/ble/GapAdvertisingData.h b/features/FEATURE_BLE/ble/GapAdvertisingData.h index 991c144f57..6f8563eaa8 100644 --- a/features/FEATURE_BLE/ble/GapAdvertisingData.h +++ b/features/FEATURE_BLE/ble/GapAdvertisingData.h @@ -537,7 +537,8 @@ public: GapAdvertisingData(void) : _payload(), _payloadLen(0), - _appearance(GENERIC_TAG) { + _appearance(GENERIC_TAG), + _minimiseFragmentation(false) { } /** @@ -724,6 +725,14 @@ public: return NULL; } + void setMinimiseFragmentation(bool enable = true) { + _minimiseFragmentation = enable; + } + + bool getMinimiseFragmentation() { + return _minimiseFragmentation; + } + private: /** * Append advertising data based on the specified type. @@ -913,6 +922,8 @@ private: * Appearance value. */ uint16_t _appearance; + + bool _minimiseFragmentation; }; /** diff --git a/features/FEATURE_BLE/ble/GapAdvertisingParams.h b/features/FEATURE_BLE/ble/GapAdvertisingParams.h index bfc1eaf147..6f438f9e7e 100644 --- a/features/FEATURE_BLE/ble/GapAdvertisingParams.h +++ b/features/FEATURE_BLE/ble/GapAdvertisingParams.h @@ -285,7 +285,7 @@ class GapExtendedAdvertisingParams { * * @param[in] newAdvType The new advertising type. */ - void setAdvertisingType(ble::advertising_type_t newAdvType) { + void setType(ble::advertising_type_t newAdvType) { _advType = newAdvType; } @@ -294,7 +294,7 @@ class GapExtendedAdvertisingParams { * * @return Advertising type. */ - ble::advertising_type_t setAdvertisingType() { + ble::advertising_type_t getType() { return _advType; } @@ -308,7 +308,7 @@ class GapExtendedAdvertisingParams { _anonymous = enable; } - ble_error_t getPrimaryAdvertisingInterval( + ble_error_t getPrimaryInterval( uint32_t *min /* ms */, uint32_t *max /* ms */ ) { @@ -320,7 +320,7 @@ class GapExtendedAdvertisingParams { return BLE_ERROR_NONE; } - void setPrimaryAdvertisingInterval( + void setPrimaryInterval( uint32_t min /* ms */, uint32_t max /* ms */ ) { @@ -328,7 +328,7 @@ class GapExtendedAdvertisingParams { _maxInterval = max; } - ble_error_t getPrimaryAdvertisingChannels( + ble_error_t getPrimaryChannels( bool *channel37, bool *channel38, bool *channel39 @@ -342,7 +342,7 @@ class GapExtendedAdvertisingParams { return BLE_ERROR_NONE; } - void setPrimaryAdvertisingChannels( + void setPrimaryChannels( bool channel37, bool channel38, bool channel39 @@ -382,27 +382,27 @@ class GapExtendedAdvertisingParams { _peerAddressType = addressType; }; - ble::advertising_policy_mode_t getAdvertisingPolicyMode() { + ble::advertising_policy_mode_t getPolicyMode() { return _policy; } - void setAdvertisingPolicyMode( + void setPolicyMode( ble::advertising_policy_mode_t mode ) { _policy = mode; } - int8_t getAdvertisingTxPower() { + int8_t getTxPower() { return _txPower; } - void setAdvertisingTxPower( + void setTxPower( int8_t txPower ) { _txPower = txPower; } - ble_error_t getAdvertisingPhy( + ble_error_t getPhy( ble::phy_t *primaryPhy, ble::phy_t *secondaryPhy ) { @@ -414,7 +414,7 @@ class GapExtendedAdvertisingParams { return BLE_ERROR_NONE; } - void setAdvertisingPhy( + void setPhy( ble::phy_t primaryPhy, ble::phy_t secondaryPhy ) { @@ -422,29 +422,33 @@ class GapExtendedAdvertisingParams { _secondaryPhy = secondaryPhy; } - uint8_t getSecondaryAdvertisingMaxSkip() { + uint8_t getSecondaryMaxSkip() { return _eventNumber; } - void setSecondaryAdvertisingMaxSkip( + void setSecondaryMaxSkip( uint8_t eventNumber ) { _eventNumber = eventNumber; } - void enableScanRequestNotification( - bool enable + void setScanRequestNotification( + bool enable = true ) { _notifyOnScan = enable; } + bool getScanRequestNotification() { + return _notifyOnScan; + } + /**/ - uint32_t getMinPrimaryAdvertisingInterval() const { + uint32_t getMinPrimaryInterval() const { return _minInterval; } - uint32_t getMinPrimaryAdvertisingInterval() const { + uint32_t getMaxPrimaryInterval() const { return _maxInterval; } @@ -456,6 +460,14 @@ class GapExtendedAdvertisingParams { return _peerAddressType; }; + ble::phy_t getPrimaryPhy() { + return _primaryPhy; + } + + ble::phy_t getSecondaryPhy() { + return _secondaryPhy; + } + private: ble::advertising_type_t _advType; uint32_t _minInterval; diff --git a/features/FEATURE_BLE/ble/generic/GenericGap.h b/features/FEATURE_BLE/ble/generic/GenericGap.h index 746371ebcd..7cc2fd1dcd 100644 --- a/features/FEATURE_BLE/ble/generic/GenericGap.h +++ b/features/FEATURE_BLE/ble/generic/GenericGap.h @@ -47,6 +47,8 @@ class GenericGap : public ::Gap, public pal::Gap::EventHandler { public: + /* TODO: move to config */ + static const size_t MAX_ADVERTISING_SETS = 64; /** * Construct a GenericGap instance for a given BLE instance ID. * @@ -73,6 +75,30 @@ public: */ virtual ~GenericGap(); + uint8_t getMaxAdvertisingSetNumber(); + + uint8_t getMaxAdvertisingDataLength(); + + ble_error_t createAdvertisingSet(AdvHandle_t* handle); + + ble_error_t destroyAdvertisingSet(AdvHandle_t handle); + + ble_error_t setAdvertisingParams(AdvHandle_t handle, const GapAdvertisingParams* params); + + ble_error_t setAdvertisingParams(AdvHandle_t handle, const GapExtendedAdvertisingParams* params); + + ble_error_t setAdvertisingPayload(AdvHandle_t handle, const GapAdvertisingData* payload); + + ble_error_t setAdvertisingScanResponse(AdvHandle_t handle, const GapAdvertisingData* response); + + ble_error_t startAdvertising(AdvHandle_t handle, uint8_t maxEvents = 0, uint32_t maxDuration = 0); + + ble_error_t stopAdvertising(AdvHandle_t handle); + + bool isAdvertisingActive(AdvHandle_t handle) const; + + void init_extended_advertising(); + /** * @see Gap::setAddress */ @@ -316,7 +342,9 @@ public: /** * @see Gap::startAdvertising */ - virtual ble_error_t startAdvertising(const GapAdvertisingParams ¶ms); + virtual ble_error_t startAdvertising( + const GapAdvertisingParams ¶ms + ); /** * @see Gap::reset @@ -433,6 +461,42 @@ private: mbed::Timeout _scan_timeout; mbed::Ticker _address_rotation_ticker; pal::ConnectionEventMonitor::EventHandler *_connection_event_handler; + uint8_t _existing_sets[(MAX_ADVERTISING_SETS / 8) + 1]; + uint8_t _active_sets[(MAX_ADVERTISING_SETS / 8) + 1]; + +private: + static bool get_adv_set_bit(const uint8_t *bytes, uint8_t bit_number) { + if (bit_number > MAX_ADVERTISING_SETS) { + return false; + } + uint8_t byte = bit_number / 8; + uint8_t bit = bit_number - byte; + bytes += byte; + bool value = ((*bytes) >> bit) & 0x01; + return value; + } + + static bool set_adv_set_bit(uint8_t *bytes, uint8_t bit_number) { + if (bit_number > MAX_ADVERTISING_SETS) { + return false; + } + uint8_t byte = bit_number / 8; + uint8_t bit = bit_number - byte; + bytes += byte; + *bytes |= 0x01 >> bit; + return true; + } + + static bool clear_adv_set_bit(uint8_t *bytes, uint8_t bit_number) { + if (bit_number > MAX_ADVERTISING_SETS) { + return false; + } + uint8_t byte = bit_number / 8; + uint8_t bit = bit_number - byte; + bytes += byte + *bytes &= 0x00 >> bit; + return true; + } }; } diff --git a/features/FEATURE_BLE/source/generic/GenericGap.cpp b/features/FEATURE_BLE/source/generic/GenericGap.cpp index 94bddf9711..8bdb2648dd 100644 --- a/features/FEATURE_BLE/source/generic/GenericGap.cpp +++ b/features/FEATURE_BLE/source/generic/GenericGap.cpp @@ -427,6 +427,11 @@ GenericGap::GenericGap( _random_static_identity_address = _pal_gap.get_random_address(); _pal_gap.set_event_handler(this); + + memset(_active_sets, 0, MAX_ADVERTISING_SETS); + memset(_existing_sets, 0, MAX_ADVERTISING_SETS); + /* legacy advertising always exists */ + *_existing_sets = 0x01; } GenericGap::~GenericGap() @@ -1109,6 +1114,7 @@ ble_error_t GenericGap::reset(void) Gap::reset(); _advertising_timeout.detach(); _scan_timeout.detach(); + _pal_gap.clear_advertising_sets(); return BLE_ERROR_NONE; } @@ -1555,5 +1561,189 @@ void GenericGap::set_connection_event_handler(pal::ConnectionEventMonitor::Event _connection_event_handler = connection_event_handler; } +uint8_t GenericGap::getMaxAdvertisingSetNumber() { + uint8_t set_number = _pal_gap.get_max_number_of_advertising_sets(); + set_number = MAX_ADVERTISING_SETS < set_number ? MAX_ADVERTISING_SETS : set_number; + return set_number; +} + +uint8_t GenericGap::getMaxAdvertisingDataLength() { + return _pal_gap.get_maximum_advertising_data_length(); +} + +ble_error_t GenericGap::createAdvertisingSet(AdvHandle_t* handle) { + uint8_t new_handle = 1; + + while (get_adv_set_bit(_existing_sets, new_handle)) { + new_handle++; + } + + if (set_adv_set_bit(_existing_sets, new_handle)) { + *handle = new_handle; + return BLE_ERROR_NONE; + } + + *handle = INVALID_ADVERTISING_HANDLE; + + return BLE_ERROR_OPERATION_NOT_PERMITTED; +} + +ble_error_t GenericGap::destroyAdvertisingSet(AdvHandle_t handle) { + if (get_adv_set_bit(_existing_sets, handle)) { + return BLE_ERROR_INVALID_PARAM; + } + + if (get_adv_set_bit(_active_sets, handle)) { + return BLE_ERROR_OPERATION_NOT_PERMITTED; + } + + if (set_adv_set_bit(_existing_sets, handle)) { + return _pal_gap.remove_advertising_set(handle); + } + + return BLE_ERROR_INVALID_PARAM; +} + +ble_error_t GenericGap::setAdvertisingParams(AdvHandle_t handle, const GapAdvertisingParams* params) { + if (handle != Gap::LEGACY_ADVERTISING_HANDLE || !params) { + return BLE_ERROR_INVALID_PARAM; + } + + pal::advertising_event_properties_t event_properties;//TODO + ble::advertising_type_t adv_type = params->getAdvertisingType(); + + AddressUseType_t use_type; + switch(adv_type) { + case ADV_SCANNABLE_UNDIRECTED: + case ADV_NON_CONNECTABLE_UNDIRECTED: + case EXT_ADV_NON_CONNECTABLE_DIRECTED: + case EXT_ADV_SCANNABLE_DIRECTED: + use_type = AddressUseType_t::PERIPHERAL_NON_CONNECTABLE + break; + default: + use_type = AddressUseType_t::PERIPHERAL_CONNECTABLE; + } + + return _pal_gap.set_extended_advertising_parameters( + (pal::advertising_handle_t)Gap::LEGACY_ADVERTISING_HANDLE, + event_properties, + (pal::advertising_interval_t)params->getIntervalInADVUnits(), + (pal::advertising_interval_t)params->getIntervalInADVUnits(), + pal::advertising_channel_map_t::ALL_ADVERTISING_CHANNELS, + (pal::own_address_type_t)get_own_address_type(use_type), + pal::advertising_peer_address_type_t::PUBLIC_ADDRESS, + params->getPeerAddress(), + (pal::advertising_filter_policy_t)_advertising_filter_policy, + (pal::advertising_power_t)params->getTxPower(), + params->getPrimaryPhy(), + params->getSecondaryMaxSkip(), + params->getSecondaryPhy(), + 0, + params->getScanRequestNotification() + ); +} + +ble_error_t GenericGap::setAdvertisingParams(AdvHandle_t handle, const GapExtendedAdvertisingParams* params) { + if (!get_adv_set_bit(_existing_sets, handle) || !params) { + return BLE_ERROR_INVALID_PARAM; + } + + pal::advertising_channel_map_t channel_map; /*TODO translate*/ + uint8_t sid;//TODO + pal::advertising_event_properties_t event_properties;//TODO + //params->getAdvertisingType() + + return _pal_gap.set_extended_advertising_parameters( + (pal::advertising_handle_t)handle, + event_properties, + (pal::advertising_interval_t)params->getMinPrimaryInterval(), + (pal::advertising_interval_t)params->getMaxPrimaryInterval(), + channel_map, + (pal::own_address_type_t)params->getOwnAddressType(), + (pal::advertising_peer_address_type_t)params->getPeerAddressType(), + params->getPeerAddress(), + (pal::advertising_filter_policy_t)params->getPolicyMode(), + (pal::advertising_power_t)params->getTxPower(), + params->getPrimaryPhy(), + params->getSecondaryMaxSkip(), + params->getSecondaryPhy(), + sid, + params->getScanRequestNotification() + ); +} + +ble_error_t GenericGap::setAdvertisingPayload(AdvHandle_t handle, const GapAdvertisingData* payload) { + if (!get_adv_set_bit(_existing_sets, handle) || !payload) { + return BLE_ERROR_INVALID_PARAM; + } + + return _pal_gap.set_extended_advertising_data( + handle, + /*TODO fragment*/ pal::advertising_fragment_description_t::FIRST_FRAGMENT, + payload->setMinimiseFragmentation, + payload->getPayloadLen(), + payload->getPayload() + ); +} + +ble_error_t GenericGap::setAdvertisingScanResponse(AdvHandle_t handle, const GapAdvertisingData* response) { + if (!get_adv_set_bit(_existing_sets, handle) || !response) { + return BLE_ERROR_INVALID_PARAM; + } + + return _pal_gap.set_extended_scan_response_data( + handle, + /*TODO fragment*/ pal::advertising_fragment_description_t::FIRST_FRAGMENT, + response->setMinimiseFragmentation, + response->getPayloadLen(), + response->getPayload() + ); +} + +ble_error_t GenericGap::startAdvertising( + AdvHandle_t handle, + uint8_t maxEvents, + uint32_t maxDuration +) { + if (!get_adv_set_bit(_existing_sets, handle)) { + return BLE_ERROR_INVALID_PARAM; + } + + /* round up */ + uint16_t duration_10ms = maxDuration ? (maxDuration - 1) / 10 + 1 : 0 ; + + ble_error_t status = _pal_gap.extended_advertising_enable( + true, + 1, + &handle, + &duration_10ms, + &maxEvents + ); + + if (status == BLE_ERROR_NONE) { + set_adv_set_bit(_active_sets, handle); + } + + return status; +} + +ble_error_t GenericGap::stopAdvertising(AdvHandle_t handle) { + if (get_adv_set_bit(_existing_sets, handle)) { + return BLE_ERROR_INVALID_PARAM; + } + + return _pal_gap.extended_advertising_enable( + true, + 1, + &handle, + NULL, + NULL + ); +} + +bool GenericGap::isAdvertisingActive(AdvHandle_t handle) const { + return get_adv_set_bit(_active_sets, handle); +} + } // namespace generic } // namespace ble