blob: 865ceeb6b9734da449d189b8535913c16488fef2 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* 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 "model/controller/le_advertiser.h"
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
#include "hci/address_with_type.h"
#include "log.h"
#include "model/controller/link_layer_controller.h"
#include "packets/hci_packets.h"
#include "packets/link_layer_packets.h"
using namespace bluetooth::hci;
using namespace std::literals;
namespace rootcanal {
namespace chrono {
using duration = std::chrono::steady_clock::duration;
using time_point = std::chrono::steady_clock::time_point;
}; // namespace chrono
slots operator"" _slots(unsigned long long count) { return slots(count); }
// =============================================================================
// Constants
// =============================================================================
// Vol 6, Part B § 4.4.2.4.3 High duty cycle connectable directed advertising.
const chrono::duration adv_direct_ind_high_timeout = 1280ms;
const chrono::duration adv_direct_ind_high_interval = 3750us;
// Vol 6, Part B § 2.3.4.9 Host Advertising Data.
const uint16_t max_legacy_advertising_pdu_size = 31;
const uint16_t max_extended_advertising_pdu_size = 1650;
// =============================================================================
// Legacy Advertising Commands
// =============================================================================
// HCI command LE_Set_Advertising_Parameters (Vol 4, Part E § 7.8.5).
ErrorCode LinkLayerController::LeSetAdvertisingParameters(
uint16_t advertising_interval_min, uint16_t advertising_interval_max,
AdvertisingType advertising_type, OwnAddressType own_address_type,
PeerAddressType peer_address_type, Address peer_address,
uint8_t advertising_channel_map,
AdvertisingFilterPolicy advertising_filter_policy) {
// Legacy advertising commands are disallowed when extended advertising
// commands were used since the last reset.
if (!SelectLegacyAdvertising()) {
INFO(id_,
"legacy advertising command rejected because extended advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// Clear reserved bits.
advertising_channel_map &= 0x7;
// For high duty cycle directed advertising, i.e. when
// Advertising_Type is 0x01 (ADV_DIRECT_IND, high duty cycle),
// the Advertising_Interval_Min and Advertising_Interval_Max parameters
// are not used and shall be ignored.
if (advertising_type == AdvertisingType::ADV_DIRECT_IND_HIGH) {
advertising_interval_min = 0x800; // Default interval value
advertising_interval_max = 0x800;
}
// The Host shall not issue this command when advertising is enabled in the
// Controller; if it is the Command Disallowed error code shall be used.
if (legacy_advertiser_.advertising_enable) {
INFO(id_, "legacy advertising is enabled");
return ErrorCode::COMMAND_DISALLOWED;
}
// At least one channel bit shall be set in the
// Advertising_Channel_Map parameter.
if (advertising_channel_map == 0) {
INFO(id_,
"advertising_channel_map (0x{:04x}) does not enable any"
" advertising channel",
advertising_channel_map);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the advertising interval range provided by the Host
// (Advertising_Interval_Min, Advertising_Interval_Max) is outside the
// advertising interval range supported by the Controller, then the
// Controller shall return the Unsupported Feature or Parameter Value (0x11)
// error code.
if (advertising_interval_min < 0x0020 || advertising_interval_min > 0x4000 ||
advertising_interval_max < 0x0020 || advertising_interval_max > 0x4000) {
INFO(id_,
"advertising_interval_min (0x{:04x}) and/or"
" advertising_interval_max (0x{:04x}) are outside the range"
" of supported values (0x0020 - 0x4000)",
advertising_interval_min, advertising_interval_max);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The Advertising_Interval_Min shall be less than or equal to the
// Advertising_Interval_Max.
if (advertising_interval_min > advertising_interval_max) {
INFO(id_,
"advertising_interval_min (0x{:04x}) is larger than"
" advertising_interval_max (0x{:04x})",
advertising_interval_min, advertising_interval_max);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
legacy_advertiser_.advertising_interval =
advertising_type == AdvertisingType::ADV_DIRECT_IND_HIGH
? std::chrono::duration_cast<slots>(adv_direct_ind_high_interval)
: slots(advertising_interval_min);
legacy_advertiser_.advertising_type = advertising_type;
legacy_advertiser_.own_address_type = own_address_type;
legacy_advertiser_.peer_address_type = peer_address_type;
legacy_advertiser_.peer_address = peer_address;
legacy_advertiser_.advertising_channel_map = advertising_channel_map;
legacy_advertiser_.advertising_filter_policy = advertising_filter_policy;
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Advertising_Data (Vol 4, Part E § 7.8.7).
ErrorCode LinkLayerController::LeSetAdvertisingData(
const std::vector<uint8_t>& advertising_data) {
// Legacy advertising commands are disallowed when extended advertising
// commands were used since the last reset.
if (!SelectLegacyAdvertising()) {
INFO(id_,
"legacy advertising command rejected because extended advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
legacy_advertiser_.advertising_data = advertising_data;
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Scan_Response_Data (Vol 4, Part E § 7.8.8).
ErrorCode LinkLayerController::LeSetScanResponseData(
const std::vector<uint8_t>& scan_response_data) {
// Legacy advertising commands are disallowed when extended advertising
// commands were used since the last reset.
if (!SelectLegacyAdvertising()) {
INFO(id_,
"legacy advertising command rejected because extended advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
legacy_advertiser_.scan_response_data = scan_response_data;
return ErrorCode::SUCCESS;
}
// HCI command LE_Advertising_Enable (Vol 4, Part E § 7.8.9).
ErrorCode LinkLayerController::LeSetAdvertisingEnable(bool advertising_enable) {
// Legacy advertising commands are disallowed when extended advertising
// commands were used since the last reset.
if (!SelectLegacyAdvertising()) {
INFO(id_,
"legacy advertising command rejected because extended advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
if (!advertising_enable) {
legacy_advertiser_.Disable();
return ErrorCode::SUCCESS;
}
AddressWithType peer_address = PeerDeviceAddress(
legacy_advertiser_.peer_address, legacy_advertiser_.peer_address_type);
AddressWithType public_address{address_, AddressType::PUBLIC_DEVICE_ADDRESS};
AddressWithType random_address{random_address_,
AddressType::RANDOM_DEVICE_ADDRESS};
std::optional<AddressWithType> resolvable_address =
GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local);
// TODO: additional checks would apply in the case of a LE only Controller
// with no configured public device address.
switch (legacy_advertiser_.own_address_type) {
case OwnAddressType::PUBLIC_DEVICE_ADDRESS:
legacy_advertiser_.advertising_address = public_address;
break;
case OwnAddressType::RANDOM_DEVICE_ADDRESS:
// If Advertising_Enable is set to 0x01, the advertising parameters'
// Own_Address_Type parameter is set to 0x01, and the random address for
// the device has not been initialized using the HCI_LE_Set_Random_Address
// command, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (random_address.GetAddress() == Address::kEmpty) {
INFO(id_,
"own_address_type is Random_Device_Address but the Random_Address"
" has not been initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
legacy_advertiser_.advertising_address = random_address;
break;
case OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
legacy_advertiser_.advertising_address =
resolvable_address.value_or(public_address);
break;
case OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
// If Advertising_Enable is set to 0x01, the advertising parameters'
// Own_Address_Type parameter is set to 0x03, the controller's resolving
// list did not contain a matching entry, and the random address for the
// device has not been initialized using the HCI_LE_Set_Random_Address
// command, the Controller shall return the error code Invalid HCI Command
// Parameters (0x12).
if (resolvable_address) {
legacy_advertiser_.advertising_address = resolvable_address.value();
} else if (random_address.GetAddress() == Address::kEmpty) {
INFO(id_,
"own_address_type is Resolvable_Or_Random_Address but the"
" Resolving_List does not contain a matching entry and the"
" Random_Address is not initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
} else {
legacy_advertiser_.advertising_address = random_address;
}
break;
}
legacy_advertiser_.timeout = {};
legacy_advertiser_.target_address =
AddressWithType{Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS};
switch (legacy_advertiser_.advertising_type) {
case AdvertisingType::ADV_DIRECT_IND_HIGH:
// The Link Layer shall exit the Advertising state no later than 1.28 s
// after the Advertising state was entered.
legacy_advertiser_.timeout =
std::chrono::steady_clock::now() + adv_direct_ind_high_timeout;
[[fallthrough]];
case AdvertisingType::ADV_DIRECT_IND_LOW: {
// Note: Vol 6, Part B § 6.2.2 Connectable directed event type
//
// If an IRK is available in the Link Layer Resolving
// List for the peer device, then the target’s device address
// (TargetA field) shall use a resolvable private address. If an IRK is
// not available in the Link Layer Resolving List or the IRK is set to
// zero for the peer device, then the target’s device address
// (TargetA field) shall use the Identity Address when entering the
// Advertising State and using connectable directed events.
std::optional<AddressWithType> peer_resolvable_address =
GenerateResolvablePrivateAddress(peer_address, IrkSelection::Peer);
legacy_advertiser_.target_address =
peer_resolvable_address.value_or(peer_address);
break;
}
default:
break;
}
legacy_advertiser_.advertising_enable = true;
legacy_advertiser_.next_event = std::chrono::steady_clock::now() +
legacy_advertiser_.advertising_interval;
return ErrorCode::SUCCESS;
}
// =============================================================================
// Extended Advertising Commands
// =============================================================================
// HCI command LE_Set_Advertising_Set_Random_Address (Vol 4, Part E § 7.8.52).
ErrorCode LinkLayerController::LeSetAdvertisingSetRandomAddress(
uint8_t advertising_handle, Address random_address) {
// If the advertising set corresponding to the Advertising_Handle parameter
// does not exist, then the Controller shall return the error code
// Unknown Advertising Identifier (0x42).
// TODO(c++20) unordered_map<>::contains
if (extended_advertisers_.count(advertising_handle) == 0) {
INFO(id_, "no advertising set defined with handle {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
// If the Host issues this command while the advertising set identified by the
// Advertising_Handle parameter is using connectable advertising and is
// enabled, the Controller shall return the error code
// Command Disallowed (0x0C).
if (advertiser.advertising_enable) {
INFO(id_, "advertising is enabled for the specified advertising set");
return ErrorCode::COMMAND_DISALLOWED;
}
advertiser.random_address = random_address;
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Extended_Advertising_Parameters (Vol 4, Part E § 7.8.53).
ErrorCode LinkLayerController::LeSetExtendedAdvertisingParameters(
uint8_t advertising_handle,
AdvertisingEventProperties advertising_event_properties,
uint16_t primary_advertising_interval_min,
uint16_t primary_advertising_interval_max,
uint8_t primary_advertising_channel_map, OwnAddressType own_address_type,
PeerAddressType peer_address_type, Address peer_address,
AdvertisingFilterPolicy advertising_filter_policy,
uint8_t advertising_tx_power, PrimaryPhyType primary_advertising_phy,
uint8_t secondary_max_skip, SecondaryPhyType secondary_advertising_phy,
uint8_t advertising_sid, bool scan_request_notification_enable) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
if (!SelectExtendedAdvertising()) {
INFO(id_,
"extended advertising command rejected because legacy advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
bool legacy_advertising = advertising_event_properties.legacy_;
bool extended_advertising = !advertising_event_properties.legacy_;
bool connectable_advertising = advertising_event_properties.connectable_;
bool scannable_advertising = advertising_event_properties.scannable_;
bool directed_advertising = advertising_event_properties.directed_;
bool high_duty_cycle_advertising =
advertising_event_properties.high_duty_cycle_;
bool anonymous_advertising = advertising_event_properties.anonymous_;
uint16_t raw_advertising_event_properties =
ExtendedAdvertiser::GetRawAdvertisingEventProperties(
advertising_event_properties);
// Clear reserved bits.
primary_advertising_channel_map &= 0x7;
// If the Advertising_Handle does not identify an existing advertising set
// and the Controller is unable to support a new advertising set at present,
// the Controller shall return the error code Memory Capacity Exceeded (0x07).
ExtendedAdvertiser advertiser(advertising_handle);
// TODO(c++20) unordered_map<>::contains
if (extended_advertisers_.count(advertising_handle) == 0) {
if (extended_advertisers_.size() >=
properties_.le_num_supported_advertising_sets) {
INFO(id_,
"no advertising set defined with handle {:02x} and"
" cannot allocate any more advertisers",
static_cast<int>(advertising_handle));
return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
}
} else {
advertiser = extended_advertisers_[advertising_handle];
}
// If the Host issues this command when advertising is enabled for the
// specified advertising set, the Controller shall return the error code
// Command Disallowed (0x0C).
if (advertiser.advertising_enable) {
INFO(id_, "advertising is enabled for the specified advertising set");
return ErrorCode::COMMAND_DISALLOWED;
}
// If legacy advertising PDU types are being used, then the parameter value
// shall be one of those specified in Table 7.2.
if (legacy_advertising &&
(raw_advertising_event_properties & ~0x10) !=
static_cast<uint16_t>(LegacyAdvertisingEventProperties::ADV_IND) &&
(raw_advertising_event_properties & ~0x10) !=
static_cast<uint16_t>(
LegacyAdvertisingEventProperties::ADV_DIRECT_IND_LOW) &&
(raw_advertising_event_properties & ~0x10) !=
static_cast<uint16_t>(
LegacyAdvertisingEventProperties::ADV_DIRECT_IND_HIGH) &&
(raw_advertising_event_properties & ~0x10) !=
static_cast<uint16_t>(
LegacyAdvertisingEventProperties::ADV_SCAN_IND) &&
(raw_advertising_event_properties & ~0x10) !=
static_cast<uint16_t>(
LegacyAdvertisingEventProperties::ADV_NONCONN_IND)) {
INFO(id_,
"advertising_event_properties (0x{:02x}) is legacy but does not"
" match valid legacy advertising event types",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
bool can_have_advertising_data =
(legacy_advertising && !directed_advertising) ||
(extended_advertising && !scannable_advertising);
// If the Advertising_Event_Properties parameter [..] specifies a type that
// does not support advertising data when the advertising set already
// contains some, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (!can_have_advertising_data && !advertiser.advertising_data.empty()) {
INFO(id_,
"advertising_event_properties (0x{:02x}) specifies an event type"
" that does not support avertising data but the set contains some",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// Note: not explicitly specified in the specification but makes sense
// in the context of the other checks.
if (!scannable_advertising && !advertiser.scan_response_data.empty()) {
INFO(id_,
"advertising_event_properties (0x{:02x}) specifies an event type"
" that does not support scan response data but the set contains some",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the advertising set already contains data, the type shall be one that
// supports advertising data and the amount of data shall not
// exceed 31 octets.
if (legacy_advertising &&
(advertiser.advertising_data.size() > max_legacy_advertising_pdu_size ||
advertiser.scan_response_data.size() >
max_legacy_advertising_pdu_size)) {
INFO(id_,
"advertising_event_properties (0x{:02x}) is legacy and the"
" advertising data or scan response data exceeds the capacity"
" of legacy PDUs",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If extended advertising PDU types are being used (bit 4 = 0) then:
// The advertisement shall not be both connectable and scannable.
if (extended_advertising && connectable_advertising &&
scannable_advertising) {
INFO(id_,
"advertising_event_properties (0x{:02x}) is extended and may not"
" be connectable and scannable at the same time",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// High duty cycle directed connectable advertising (≤ 3.75 ms
// advertising interval) shall not be used (bit 3 = 0).
if (extended_advertising && connectable_advertising && directed_advertising &&
high_duty_cycle_advertising) {
INFO(id_,
"advertising_event_properties (0x{:02x}) is extended and may not"
" be high-duty cycle directed connectable",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the primary advertising interval range provided by the Host
// (Primary_Advertising_Interval_Min, Primary_Advertising_Interval_Max) is
// outside the advertising interval range supported by the Controller, then
// the Controller shall return the error code Unsupported Feature or
// Parameter Value (0x11).
if (primary_advertising_interval_min < 0x20 ||
primary_advertising_interval_max < 0x20) {
INFO(id_,
"primary_advertising_interval_min (0x{:04x}) and/or"
" primary_advertising_interval_max (0x{:04x}) are outside the range"
" of supported values (0x0020 - 0xffff)",
primary_advertising_interval_min, primary_advertising_interval_max);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The Primary_Advertising_Interval_Min parameter shall be less than or equal
// to the Primary_Advertising_Interval_Max parameter.
if (primary_advertising_interval_min > primary_advertising_interval_max) {
INFO(id_,
"primary_advertising_interval_min (0x{:04x}) is larger than"
" primary_advertising_interval_max (0x{:04x})",
primary_advertising_interval_min, primary_advertising_interval_max);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// At least one channel bit shall be set in the
// Primary_Advertising_Channel_Map parameter.
if (primary_advertising_channel_map == 0) {
INFO(id_,
"primary_advertising_channel_map does not enable any"
" advertising channel");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If legacy advertising PDUs are being used, the
// Primary_Advertising_PHY shall indicate the LE 1M PHY.
if (legacy_advertising && primary_advertising_phy != PrimaryPhyType::LE_1M) {
INFO(id_,
"advertising_event_properties (0x{:04x}) is legacy but"
" primary_advertising_phy ({:02x}) is not LE 1M",
raw_advertising_event_properties,
static_cast<uint8_t>(primary_advertising_phy));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If Constant Tone Extensions are enabled for the advertising set and
// Secondary_Advertising_PHY specifies a PHY that does not allow
// Constant Tone Extensions, the Controller shall
// return the error code Command Disallowed (0x0C).
if (advertiser.constant_tone_extensions &&
secondary_advertising_phy == SecondaryPhyType::LE_CODED) {
INFO(id_,
"constant tone extensions are enabled but"
" secondary_advertising_phy ({:02x}) does not support them",
static_cast<uint8_t>(secondary_advertising_phy));
return ErrorCode::COMMAND_DISALLOWED;
}
// If the Host issues this command when periodic advertising is enabled for
// the specified advertising set and connectable, scannable, legacy,
// or anonymous advertising is specified, the Controller shall return the
// error code Invalid HCI Command Parameters (0x12).
if (advertiser.periodic_advertising_enable &&
(connectable_advertising || scannable_advertising || legacy_advertising ||
anonymous_advertising)) {
INFO(id_,
"periodic advertising is enabled for the specified advertising set"
" and advertising_event_properties (0x{:02x}) is either"
" connectable, scannable, legacy, or anonymous",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If periodic advertising is enabled for the advertising set and the
// Secondary_Advertising_PHY parameter does not specify the PHY currently
// being used for the periodic advertising, the Controller shall return the
// error code Command Disallowed (0x0C).
#if 0
if (advertiser.periodic_advertising_enable) {
// TODO
INFO(id_,
"periodic advertising is enabled for the specified advertising set"
" and the secondary PHY does not match the periodic"
" advertising PHY");
return ErrorCode::COMMAND_DISALLOWED;
}
#endif
// If the advertising set already contains advertising data or scan response
// data, extended advertising is being used, and the length of the data is
// greater than the maximum that the Controller can transmit within the
// longest possible auxiliary advertising segment consistent with the
// parameters, the Controller shall return the error code
// Packet Too Long (0x45). If advertising on the LE Coded PHY, the S=8
// coding shall be assumed.
if (extended_advertising &&
(advertiser.advertising_data.size() > max_extended_advertising_pdu_size ||
advertiser.scan_response_data.size() >
max_extended_advertising_pdu_size)) {
INFO(id_,
"the advertising data contained in the set is larger than the"
" available PDU capacity");
return ErrorCode::PACKET_TOO_LONG;
}
advertiser.advertising_event_properties = advertising_event_properties;
advertiser.primary_advertising_interval =
slots(primary_advertising_interval_min);
advertiser.primary_advertising_channel_map = primary_advertising_channel_map;
advertiser.own_address_type = own_address_type;
advertiser.peer_address_type = peer_address_type;
advertiser.peer_address = peer_address;
advertiser.advertising_filter_policy = advertising_filter_policy;
advertiser.advertising_tx_power = advertising_tx_power;
advertiser.primary_advertising_phy = primary_advertising_phy;
advertiser.secondary_max_skip = secondary_max_skip;
advertiser.secondary_advertising_phy = secondary_advertising_phy;
advertiser.advertising_sid = advertising_sid;
advertiser.scan_request_notification_enable =
scan_request_notification_enable;
extended_advertisers_.insert_or_assign(advertising_handle,
std::move(advertiser));
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Extended_Advertising_Data (Vol 4, Part E § 7.8.54).
ErrorCode LinkLayerController::LeSetExtendedAdvertisingData(
uint8_t advertising_handle, Operation operation,
FragmentPreference fragment_preference,
const std::vector<uint8_t>& advertising_data) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
if (!SelectExtendedAdvertising()) {
INFO(id_,
"extended advertising command rejected because legacy advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// fragment_preference is unused for now.
(void)fragment_preference;
// If the advertising set corresponding to the Advertising_Handle parameter
// does not exist, then the Controller shall return the error code
// Unknown Advertising Identifier (0x42).
// TODO(c++20) unordered_map<>::contains
if (extended_advertisers_.count(advertising_handle) == 0) {
INFO(id_, "no advertising set defined with handle {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
const AdvertisingEventProperties& advertising_event_properties =
advertiser.advertising_event_properties;
uint16_t raw_advertising_event_properties =
ExtendedAdvertiser::GetRawAdvertisingEventProperties(
advertising_event_properties);
bool can_have_advertising_data = (advertising_event_properties.legacy_ &&
!advertising_event_properties.directed_) ||
(!advertising_event_properties.legacy_ &&
!advertising_event_properties.scannable_);
// If the advertising set specifies a type that does not support
// advertising data, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (!can_have_advertising_data) {
INFO(id_,
"advertising_event_properties ({:02x}) does not support"
" advertising data",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the advertising set uses legacy advertising PDUs that support
// advertising data and either Operation is not 0x03 or the
// Advertising_Data_Length parameter exceeds 31 octets, the Controller
// shall return the error code Invalid HCI Command Parameters (0x12).
if (advertising_event_properties.legacy_ &&
(operation != Operation::COMPLETE_ADVERTISEMENT ||
advertising_data.size() > max_legacy_advertising_pdu_size)) {
INFO(id_,
"advertising_event_properties ({:02x}) is legacy and"
" and an incomplete operation was used or the advertising data"
" is larger than 31",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If Operation is 0x04 and:
// • advertising is currently disabled for the advertising set;
// • the advertising set contains no data;
// • the advertising set uses legacy PDUs; or
// • Advertising_Data_Length is not zero;
// then the Controller shall return the error code Invalid HCI Command
// Parameters (0x12).
if (operation == Operation::UNCHANGED_DATA &&
(!advertiser.advertising_enable || advertiser.advertising_data.empty() ||
advertising_event_properties.legacy_ || !advertising_data.empty())) {
INFO(id_,
"Unchanged_Data operation is used but advertising is disabled;"
" or the advertising set contains no data;"
" or the advertising set uses legacy PDUs;"
" or the advertising data is not empty");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If Operation is not 0x03 or 0x04 and Advertising_Data_Length is zero,
// the Controller shall return the error code Invalid HCI
// Command Parameters (0x12).
if (operation != Operation::COMPLETE_ADVERTISEMENT &&
operation != Operation::UNCHANGED_DATA && advertising_data.empty()) {
INFO(id_,
"operation ({:02x}) is not Complete_Advertisement or Unchanged_Data"
" but the advertising data is empty",
static_cast<int>(operation));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If advertising is currently enabled for the specified advertising set and
// Operation does not have the value 0x03 or 0x04, the Controller shall
// return the error code Command Disallowed (0x0C).
if (advertiser.advertising_enable &&
operation != Operation::COMPLETE_ADVERTISEMENT &&
operation != Operation::UNCHANGED_DATA) {
INFO(id_,
"operation ({:02x}) is used but advertising is enabled for the"
" specified advertising set",
static_cast<int>(operation));
return ErrorCode::COMMAND_DISALLOWED;
}
switch (operation) {
case Operation::INTERMEDIATE_FRAGMENT:
advertiser.advertising_data.insert(advertiser.advertising_data.end(),
advertising_data.begin(),
advertising_data.end());
advertiser.partial_advertising_data = true;
break;
case Operation::FIRST_FRAGMENT:
advertiser.advertising_data = advertising_data;
advertiser.partial_advertising_data = true;
break;
case Operation::LAST_FRAGMENT:
advertiser.advertising_data.insert(advertiser.advertising_data.end(),
advertising_data.begin(),
advertising_data.end());
advertiser.partial_advertising_data = false;
break;
case Operation::COMPLETE_ADVERTISEMENT:
advertiser.advertising_data = advertising_data;
advertiser.partial_advertising_data = false;
break;
case Operation::UNCHANGED_DATA:
break;
default:
INFO(id_, "unknown operation ({})", static_cast<int>(operation));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the combined length of the data exceeds the capacity of the
// advertising set identified by the Advertising_Handle parameter
// (see Section 7.8.57 LE Read Maximum Advertising Data Length command)
// or the amount of memory currently available, all the data
// shall be discarded and the Controller shall return the error code Memory
// Capacity Exceeded (0x07).
if (advertiser.advertising_data.size() >
properties_.le_max_advertising_data_length) {
INFO(id_,
"the combined length {} of the advertising data exceeds the"
" advertising set capacity {}",
advertiser.advertising_data.size(),
properties_.le_max_advertising_data_length);
advertiser.advertising_data.clear();
advertiser.partial_advertising_data = false;
return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
}
// If advertising is currently enabled for the specified advertising set,
// the advertising set uses extended advertising, and the length of the
// data is greater than the maximum that the Controller can transmit within
// the longest possible auxiliary advertising segment consistent with the
// current parameters of the advertising set, the Controller shall return
// the error code Packet Too Long (0x45). If advertising on the
// LE Coded PHY, the S=8 coding shall be assumed.
size_t max_advertising_data_length =
ExtendedAdvertiser::GetMaxAdvertisingDataLength(
advertising_event_properties);
if (advertiser.advertising_enable &&
advertiser.advertising_data.size() > max_advertising_data_length) {
INFO(id_,
"the advertising data contained in the set is larger than the"
" available PDU capacity");
advertiser.advertising_data.clear();
advertiser.partial_advertising_data = false;
return ErrorCode::PACKET_TOO_LONG;
}
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Extended_Scan_Response_Data (Vol 4, Part E § 7.8.55).
ErrorCode LinkLayerController::LeSetExtendedScanResponseData(
uint8_t advertising_handle, Operation operation,
FragmentPreference fragment_preference,
const std::vector<uint8_t>& scan_response_data) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
if (!SelectExtendedAdvertising()) {
INFO(id_,
"extended advertising command rejected because legacy advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// fragment_preference is unused for now.
(void)fragment_preference;
// If the advertising set corresponding to the Advertising_Handle parameter
// does not exist, then the Controller shall return the error code
// Unknown Advertising Identifier (0x42).
// TODO(c++20) unordered_map<>::contains
if (extended_advertisers_.count(advertising_handle) == 0) {
INFO(id_, "no advertising set defined with handle {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
const AdvertisingEventProperties& advertising_event_properties =
advertiser.advertising_event_properties;
uint16_t raw_advertising_event_properties =
ExtendedAdvertiser::GetRawAdvertisingEventProperties(
advertising_event_properties);
// If the advertising set is non-scannable and the Host uses this
// command other than to discard existing data, the Controller shall
// return the error code Invalid HCI Command Parameters (0x12).
if (!advertising_event_properties.scannable_ && !scan_response_data.empty()) {
INFO(id_,
"advertising_event_properties ({:02x}) is not scannable"
" but the scan response data is not empty",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the advertising set uses scannable legacy advertising PDUs and
// either Operation is not 0x03 or the Scan_Response_Data_Length
// parameter exceeds 31 octets, the Controller shall
// return the error code Invalid HCI Command Parameters (0x12).
if (advertising_event_properties.scannable_ &&
advertising_event_properties.legacy_ &&
(operation != Operation::COMPLETE_ADVERTISEMENT ||
scan_response_data.size() > max_legacy_advertising_pdu_size)) {
INFO(id_,
"advertising_event_properties ({:02x}) is scannable legacy"
" and an incomplete operation was used or the scan response data"
" is larger than 31",
raw_advertising_event_properties);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If Operation is not 0x03 and Scan_Response_Data_Length is zero, the
// Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (operation != Operation::COMPLETE_ADVERTISEMENT &&
scan_response_data.empty()) {
INFO(id_,
"operation ({:02x}) is not Complete_Advertisement but the"
" scan response data is empty",
static_cast<int>(operation));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If advertising is currently enabled for the specified advertising set and
// Operation does not have the value 0x03, the Controller shall
// return the error code Command Disallowed (0x0C).
if (advertiser.advertising_enable &&
operation != Operation::COMPLETE_ADVERTISEMENT) {
INFO(id_,
"operation ({:02x}) is used but advertising is enabled for the"
" specified advertising set",
static_cast<int>(operation));
return ErrorCode::COMMAND_DISALLOWED;
}
// If the advertising set uses scannable extended advertising PDUs,
// advertising is currently enabled for the specified advertising set,
// and Scan_Response_Data_Length is zero, the Controller shall return
// the error code Command Disallowed (0x0C).
if (advertiser.advertising_enable &&
advertising_event_properties.scannable_ &&
!advertising_event_properties.legacy_ && scan_response_data.empty()) {
INFO(id_,
"advertising_event_properties ({:02x}) is scannable extended,"
" advertising is enabled for the specified advertising set"
" and the scan response data is empty",
raw_advertising_event_properties);
return ErrorCode::COMMAND_DISALLOWED;
}
switch (operation) {
case Operation::INTERMEDIATE_FRAGMENT:
advertiser.scan_response_data.insert(advertiser.scan_response_data.end(),
scan_response_data.begin(),
scan_response_data.end());
advertiser.partial_scan_response_data = true;
break;
case Operation::FIRST_FRAGMENT:
advertiser.scan_response_data = scan_response_data;
advertiser.partial_scan_response_data = true;
break;
case Operation::LAST_FRAGMENT:
advertiser.scan_response_data.insert(advertiser.scan_response_data.end(),
scan_response_data.begin(),
scan_response_data.end());
advertiser.partial_scan_response_data = false;
break;
case Operation::COMPLETE_ADVERTISEMENT:
advertiser.scan_response_data = scan_response_data;
advertiser.partial_scan_response_data = false;
break;
case Operation::UNCHANGED_DATA:
INFO(id_,
"the operation Unchanged_Data is only allowed"
" for Advertising_Data");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
default:
INFO(id_, "unknown operation ({})", static_cast<int>(operation));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the combined length of the data exceeds the capacity of the
// advertising set identified by the Advertising_Handle parameter
// (see Section 7.8.57 LE Read Maximum Advertising Data Length command)
// or the amount of memory currently available, all the data shall be
// discarded and the Controller shall return the error code
// Memory Capacity Exceeded (0x07).
if (advertiser.scan_response_data.size() >
properties_.le_max_advertising_data_length) {
INFO(id_,
"the combined length of the scan response data exceeds the"
" advertising set capacity");
advertiser.scan_response_data.clear();
advertiser.partial_scan_response_data = false;
return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
}
// If the advertising set uses extended advertising and the combined length
// of the data is greater than the maximum that the Controller can transmit
// within the longest possible auxiliary advertising segment consistent
// with the current parameters of the advertising set (using the current
// advertising interval if advertising is enabled), all the data shall be
// discarded and the Controller shall return the error code
// Packet Too Long (0x45). If advertising on the LE Coded PHY,
// the S=8 coding shall be assumed.
if (advertiser.scan_response_data.size() >
max_extended_advertising_pdu_size) {
INFO(id_,
"the scan response data contained in the set is larger than the"
" available PDU capacity");
advertiser.scan_response_data.clear();
advertiser.partial_scan_response_data = false;
return ErrorCode::PACKET_TOO_LONG;
}
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Extended_Advertising_Enable (Vol 4, Part E § 7.8.56).
ErrorCode LinkLayerController::LeSetExtendedAdvertisingEnable(
bool enable, const std::vector<bluetooth::hci::EnabledSet>& sets) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
if (!SelectExtendedAdvertising()) {
INFO(id_,
"extended advertising command rejected because legacy advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// Validate the advertising handles.
std::array<bool, UINT8_MAX> used_advertising_handles{};
for (auto& set : sets) {
// If the same advertising set is identified by more than one entry in the
// Advertising_Handle[i] arrayed parameter, then the Controller shall return
// the error code Invalid HCI Command Parameters (0x12).
if (used_advertising_handles[set.advertising_handle_]) {
INFO(id_, "advertising handle {:02x} is added more than once",
set.advertising_handle_);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the advertising set corresponding to the Advertising_Handle[i]
// parameter does not exist, then the Controller shall return the error code
// Unknown Advertising Identifier (0x42).
if (extended_advertisers_.find(set.advertising_handle_) ==
extended_advertisers_.end()) {
INFO(id_, "advertising handle {:02x} is not defined",
set.advertising_handle_);
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
used_advertising_handles[set.advertising_handle_] = true;
}
// If Enable and Num_Sets are both set to
// 0x00, then all advertising sets are disabled.
if (!enable && sets.empty()) {
for (auto& [_, advertiser] : extended_advertisers_) {
advertiser.Disable();
}
return ErrorCode::SUCCESS;
}
// If Num_Sets is set to 0x00, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (sets.empty()) {
INFO(id_, "enable is true but no advertising set is selected");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// No additional checks for disabling advertising sets.
if (!enable) {
for (auto& set : sets) {
auto& advertiser = extended_advertisers_[set.advertising_handle_];
advertiser.Disable();
}
return ErrorCode::SUCCESS;
}
// Validate the advertising parameters before enabling any set.
for (auto& set : sets) {
ExtendedAdvertiser& advertiser =
extended_advertisers_[set.advertising_handle_];
const AdvertisingEventProperties& advertising_event_properties =
advertiser.advertising_event_properties;
bool extended_advertising = !advertising_event_properties.legacy_;
bool connectable_advertising = advertising_event_properties.connectable_;
bool scannable_advertising = advertising_event_properties.scannable_;
bool directed_advertising = advertising_event_properties.directed_;
bool high_duty_cycle_advertising =
advertising_event_properties.high_duty_cycle_;
// If the advertising is high duty cycle connectable directed advertising,
// then Duration[i] shall be less than or equal to 1.28 seconds and shall
// not be equal to 0.
std::chrono::milliseconds duration =
std::chrono::milliseconds(set.duration_ * 10);
if (connectable_advertising && directed_advertising &&
high_duty_cycle_advertising &&
(set.duration_ == 0 || duration > adv_direct_ind_high_timeout)) {
INFO(id_,
"extended advertising is high duty cycle connectable directed"
" but the duration is either 0 or larger than 1.28 seconds");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the advertising set contains partial advertising data or partial
// scan response data, the Controller shall return the error code
// Command Disallowed (0x0C).
if (advertiser.partial_advertising_data ||
advertiser.partial_scan_response_data) {
INFO(id_,
"advertising set contains partial advertising"
" or scan response data");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the advertising set uses scannable extended advertising PDUs and no
// scan response data is currently provided, the Controller shall return the
// error code Command Disallowed (0x0C).
if (extended_advertising && scannable_advertising &&
advertiser.scan_response_data.empty()) {
INFO(id_,
"advertising set uses scannable extended advertising PDUs"
" but no scan response data is provided");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the advertising set uses connectable extended advertising PDUs and the
// advertising data in the advertising set will not fit in the
// AUX_ADV_IND PDU, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (extended_advertising && connectable_advertising &&
advertiser.advertising_data.size() >
ExtendedAdvertiser::GetMaxAdvertisingDataLength(
advertising_event_properties)) {
INFO(id_,
"advertising set uses connectable extended advertising PDUs"
" but the advertising data does not fit in AUX_ADV_IND PDUs");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If extended advertising is being used and the length of any advertising
// data or of any scan response data is greater than the maximum that the
// Controller can transmit within the longest possible auxiliary
// advertising segment consistent with the chosen advertising interval,
// the Controller shall return the error code Packet Too Long (0x45).
// If advertising on the LE Coded PHY, the S=8 coding shall be assumed.
if (extended_advertising && (advertiser.advertising_data.size() >
max_extended_advertising_pdu_size ||
advertiser.scan_response_data.size() >
max_extended_advertising_pdu_size)) {
INFO(id_,
"advertising set uses extended advertising PDUs"
" but the advertising data does not fit in advertising PDUs");
return ErrorCode::PACKET_TOO_LONG;
}
AddressWithType peer_address = PeerDeviceAddress(
advertiser.peer_address, advertiser.peer_address_type);
AddressWithType public_address{address_,
AddressType::PUBLIC_DEVICE_ADDRESS};
AddressWithType random_address{
advertiser.random_address.value_or(Address::kEmpty),
AddressType::RANDOM_DEVICE_ADDRESS};
std::optional<AddressWithType> resolvable_address =
GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local);
// TODO: additional checks would apply in the case of a LE only Controller
// with no configured public device address.
switch (advertiser.own_address_type) {
case OwnAddressType::PUBLIC_DEVICE_ADDRESS:
advertiser.advertising_address = public_address;
break;
case OwnAddressType::RANDOM_DEVICE_ADDRESS:
// If the advertising set's Own_Address_Type parameter is set to 0x01
// and the random address for the advertising set has not been
// initialized using the HCI_LE_Set_Advertising_Set_Random_Address
// command, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (random_address.GetAddress() == Address::kEmpty) {
INFO(
id_,
"own_address_type is Random_Device_Address but the Random_Address"
" has not been initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
advertiser.advertising_address = random_address;
break;
case OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
advertiser.advertising_address =
resolvable_address.value_or(public_address);
break;
case OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
// If the advertising set's Own_Address_Type parameter is set to 0x03,
// the controller's resolving list did not contain a matching entry,
// and the random address for the advertising set has not been
// initialized using the HCI_LE_Set_Advertising_Set_Random_Address
// command, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (resolvable_address) {
advertiser.advertising_address = resolvable_address.value();
} else if (random_address.GetAddress() == Address::kEmpty) {
INFO(id_,
"own_address_type is Resolvable_Or_Random_Address but the"
" Resolving_List does not contain a matching entry and the"
" Random_Address is not initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
} else {
advertiser.advertising_address = random_address;
}
break;
}
}
for (auto& set : sets) {
ExtendedAdvertiser& advertiser =
extended_advertisers_[set.advertising_handle_];
advertiser.max_extended_advertising_events =
set.max_extended_advertising_events_;
advertiser.num_completed_extended_advertising_events = 0;
advertiser.Enable();
if (set.duration_ > 0) {
std::chrono::milliseconds duration =
std::chrono::milliseconds(set.duration_ * 10);
advertiser.timeout = std::chrono::steady_clock::now() + duration;
} else {
advertiser.timeout.reset();
}
}
return ErrorCode::SUCCESS;
}
// HCI command LE_Remove_Advertising_Set (Vol 4, Part E § 7.8.59).
ErrorCode LinkLayerController::LeRemoveAdvertisingSet(
uint8_t advertising_handle) {
// If the advertising set corresponding to the Advertising_Handle parameter
// does not exist, then the Controller shall return the error code
// Unknown Advertising Identifier (0x42).
auto advertiser = extended_advertisers_.find(advertising_handle);
if (advertiser == extended_advertisers_.end()) {
INFO(id_, "no advertising set defined with handle {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
// If advertising or periodic advertising on the advertising set is
// enabled, then the Controller shall return the error code
// Command Disallowed (0x0C).
if (advertiser->second.advertising_enable) {
INFO(id_, "the advertising set defined with handle {:02x} is enabled",
static_cast<int>(advertising_handle));
return ErrorCode::COMMAND_DISALLOWED;
}
extended_advertisers_.erase(advertiser);
return ErrorCode::SUCCESS;
}
// HCI command LE_Clear_Advertising_Sets (Vol 4, Part E § 7.8.60).
ErrorCode LinkLayerController::LeClearAdvertisingSets() {
// If advertising or periodic advertising is enabled on any advertising set,
// then the Controller shall return the error code Command Disallowed (0x0C).
for (auto& advertiser : extended_advertisers_) {
if (advertiser.second.advertising_enable) {
INFO(id_, "the advertising set with handle {:02x} is enabled",
static_cast<int>(advertiser.second.advertising_enable));
return ErrorCode::COMMAND_DISALLOWED;
}
}
extended_advertisers_.clear();
return ErrorCode::SUCCESS;
}
uint16_t ExtendedAdvertiser::GetMaxAdvertisingDataLength(
const AdvertisingEventProperties& properties) {
// The PDU AdvData size is defined in the following sections:
// - Vol 6, Part B § 2.3.1.1 ADV_IND
// - Vol 6, Part B § 2.3.1.2 ADV_DIRECT_IND
// - Vol 6, Part B § 2.3.1.3 ADV_NONCONN_IND
// - Vol 6, Part B § 2.3.1.4 ADV_SCAN_IND
// - Vol 6, Part B § 2.3.1.5 ADV_EXT_IND
// - Vol 6, Part B § 2.3.1.6 AUX_ADV_IND
// - Vol 6, Part B § 2.3.1.8 AUX_CHAIN_IND
// - Vol 6, Part B § 2.3.4 Common Extended Advertising Payload Format
uint16_t max_advertising_data_length;
if (properties.legacy_ && properties.directed_) {
// Directed legacy advertising PDUs do not have AdvData payload.
max_advertising_data_length = 0;
} else if (properties.legacy_) {
max_advertising_data_length = max_legacy_advertising_pdu_size;
} else if (properties.scannable_) {
// Scannable extended advertising PDUs do not have AdvData payload.
max_advertising_data_length = 0;
} else if (!properties.connectable_) {
// When extended advertising is non-scannable and non-connectable,
// AUX_CHAIN_IND PDUs can be used, and the advertising data may be
// fragmented over multiple PDUs; the length is still capped at 1650
// as stated in Vol 6, Part B § 2.3.4.9 Host Advertising Data.
max_advertising_data_length = max_extended_advertising_pdu_size;
} else {
// When extended advertising is either scannable or connectable,
// AUX_CHAIN_IND PDUs may not be used, and the maximum advertising data
// length is 254. Extended payload header fields eat into the
// available space.
max_advertising_data_length = 254;
max_advertising_data_length -= 6; // AdvA
max_advertising_data_length -= 2; // ADI
max_advertising_data_length -= 6 * properties.directed_; // TargetA
max_advertising_data_length -= 1 * properties.tx_power_; // TxPower
// TODO(pedantic): configure the ACAD field in order to leave the least
// amount of AdvData space to the user (191).
}
return max_advertising_data_length;
}
uint16_t ExtendedAdvertiser::GetMaxScanResponseDataLength(
const AdvertisingEventProperties& properties) {
// The PDU AdvData size is defined in the following sections:
// - Vol 6, Part B § 2.3.2.2 SCAN_RSP
// - Vol 6, Part B § 2.3.2.3 AUX_SCAN_RSP
// - Vol 6, Part B § 2.3.1.8 AUX_CHAIN_IND
// - Vol 6, Part B § 2.3.4 Common Extended Advertising Payload Format
uint16_t max_scan_response_data_length;
if (!properties.scannable_) {
max_scan_response_data_length = 0;
} else if (properties.legacy_) {
max_scan_response_data_length = max_legacy_advertising_pdu_size;
} else {
// Extended scan response data may be sent over AUX_CHAIN_PDUs, and
// the advertising data may be fragmented over multiple PDUs; the length
// is still capped at 1650 as stated in
// Vol 6, Part B § 2.3.4.9 Host Advertising Data.
max_scan_response_data_length = max_extended_advertising_pdu_size;
}
return max_scan_response_data_length;
}
uint16_t ExtendedAdvertiser::GetRawAdvertisingEventProperties(
const AdvertisingEventProperties& properties) {
uint16_t mask = 0;
if (properties.connectable_) {
mask |= 0x1;
}
if (properties.scannable_) {
mask |= 0x2;
}
if (properties.directed_) {
mask |= 0x4;
}
if (properties.high_duty_cycle_) {
mask |= 0x8;
}
if (properties.legacy_) {
mask |= 0x10;
}
if (properties.anonymous_) {
mask |= 0x20;
}
if (properties.tx_power_) {
mask |= 0x40;
}
return mask;
}
// =============================================================================
// Periodic Advertising Commands
// =============================================================================
// HCI LE Set Periodic Advertising Parameters command (Vol 4, Part E § 7.8.61).
ErrorCode LinkLayerController::LeSetPeriodicAdvertisingParameters(
uint8_t advertising_handle, uint16_t periodic_advertising_interval_min,
uint16_t periodic_advertising_interval_max, bool /*include_tx_power*/) {
// The Advertising_Handle parameter identifies the advertising set whose
// periodic advertising parameters are being configured. If the corresponding
// advertising set does not already exist, then the Controller shall return
// the error code Unknown Advertising Identifier (0x42).
// TODO(c++20) unordered_map<>::contains
if (extended_advertisers_.count(advertising_handle) == 0) {
INFO(id_, "no advertising set defined with handle {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
// The Periodic_Advertising_Interval_Min parameter shall be less than or
// equal to the Periodic_Advertising_Interval_Max parameter.
if (periodic_advertising_interval_min < 0x6 ||
periodic_advertising_interval_max < 0x6 ||
periodic_advertising_interval_max < periodic_advertising_interval_min) {
INFO(id_, "invalid periodic advertising interval range {:04x} - {:04x}",
periodic_advertising_interval_min, periodic_advertising_interval_max);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the advertising set identified by the Advertising_Handle specified
// scannable, connectable, legacy, or anonymous advertising, the Controller
// shall return the error code Invalid HCI Command Parameters (0x12).
if (advertiser.advertising_event_properties.connectable_ ||
advertiser.advertising_event_properties.scannable_ ||
advertiser.advertising_event_properties.legacy_ ||
advertiser.advertising_event_properties.anonymous_) {
INFO(id_,
"the periodic advertising set {:02x} specifies scannable,"
" connectable, legacy or anonymous advertising",
static_cast<int>(advertising_handle));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the Host issues this command when periodic advertising is enabled for
// the specified advertising set, the Controller shall return the error code
// Command Disallowed (0x0C).
if (advertiser.periodic_advertising_enable) {
INFO(id_, "periodic advertising is enabled for the set {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::COMMAND_DISALLOWED;
}
// If the Advertising_Handle does not identify an advertising set that is
// already configured for periodic advertising and the Controller is unable
// to support more periodic advertising at present, the Controller shall
// return the error code Memory Capacity Exceeded (0x07)
// TODO: add controller configuration for maximum number of periodic
// advertising sets.
// If the advertising set already contains periodic advertising data and the
// length of the data is greater than the maximum that the Controller can
// transmit within a periodic advertising interval of
// Periodic_Advertising_Interval_Max, the Controller shall return the error
// code Packet Too Long (0x45).
if (advertiser.periodic_advertising_data.size() >
ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
slots(periodic_advertising_interval_max))) {
INFO(id_,
"the length of the periodic advertising data exceeds the maximum"
" that the controller can transmit within the maximum periodic"
" advertising interval");
return ErrorCode::PACKET_TOO_LONG;
}
advertiser.periodic_advertising_interval =
slots(periodic_advertising_interval_max);
return ErrorCode::SUCCESS;
}
// HCI LE Set Periodic Advertising Data command (Vol 4, Part E § 7.8.62).
ErrorCode LinkLayerController::LeSetPeriodicAdvertisingData(
uint8_t advertising_handle, bluetooth::hci::Operation operation,
const std::vector<uint8_t>& advertising_data) {
// If the advertising set corresponding to the Advertising_Handle parameter
// does not exist, then the Controller shall return the error code
// Unknown Advertising Identifier (0x42).
// TODO(c++20) unordered_map<>::contains
if (extended_advertisers_.count(advertising_handle) == 0) {
INFO(id_, "no advertising set defined with handle {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
// If the advertising set has not been configured for periodic advertising,
// then the Controller shall return the error code Command Disallowed (0x0C).
if (advertiser.periodic_advertising_interval.count() == 0) {
INFO(id_, "periodic advertising is not configured for the set {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::COMMAND_DISALLOWED;
}
// If periodic advertising is currently enabled for the specified advertising
// set and Operation does not have the value 0x03 or 0x04, then the Controller
// shall return the error code Command Disallowed (0x0C).
if (advertiser.periodic_advertising_enable &&
operation != Operation::COMPLETE_ADVERTISEMENT &&
operation != Operation::UNCHANGED_DATA) {
INFO(id_,
"periodic advertising is enabled and the operation is not"
" Complete_Advertisement or Unchanged_Data");
return ErrorCode::COMMAND_DISALLOWED;
}
// If Operation is not 0x03 or 0x04 and Advertising_Data_Length is zero,
// then the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (advertising_data.empty() &&
operation != Operation::COMPLETE_ADVERTISEMENT &&
operation != Operation::UNCHANGED_DATA) {
INFO(id_,
"periodic advertising data is empty is enabled and the operation"
" is not Complete_Advertisement or Unchanged_Data");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If Operation is 0x04 and:
// • periodic advertising is currently disabled for the advertising set;
// • the periodic advertising set contains no data; or
// • Advertising_Data_Length is not zero;
// then the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (operation == Operation::UNCHANGED_DATA &&
(!advertiser.periodic_advertising_enable ||
advertiser.periodic_advertising_data.empty() ||
!advertising_data.empty())) {
INFO(
id_,
"Unchanged_Data operation is used but periodic advertising is disabled;"
" or the periodic advertising set contains no data;"
" or the advertising data is not empty");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
switch (operation) {
case Operation::INTERMEDIATE_FRAGMENT:
advertiser.periodic_advertising_data.insert(
advertiser.periodic_advertising_data.end(), advertising_data.begin(),
advertising_data.end());
advertiser.partial_periodic_advertising_data = true;
break;
case Operation::FIRST_FRAGMENT:
advertiser.periodic_advertising_data = advertising_data;
advertiser.partial_periodic_advertising_data = true;
break;
case Operation::LAST_FRAGMENT:
advertiser.periodic_advertising_data.insert(
advertiser.periodic_advertising_data.end(), advertising_data.begin(),
advertising_data.end());
advertiser.partial_periodic_advertising_data = false;
break;
case Operation::COMPLETE_ADVERTISEMENT:
advertiser.periodic_advertising_data = advertising_data;
advertiser.partial_periodic_advertising_data = false;
break;
case Operation::UNCHANGED_DATA:
break;
default:
INFO(id_, "unknown operation ({})", static_cast<int>(operation));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the combined length of the data exceeds the capacity of the advertising
// set identified by the Advertising_Handle parameter or the amount of memory
// currently available, all the data shall be discarded and the Controller
// shall return the error code Memory Capacity Exceeded (0x07).
if (advertiser.periodic_advertising_data.size() >
properties_.le_max_advertising_data_length) {
INFO(id_,
"the length of the combined periodic advertising data exceeds"
" the maximum advertising data length");
advertiser.periodic_advertising_data.clear();
advertiser.partial_periodic_advertising_data = false;
return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
}
// If the combined length of the data is greater than the maximum that the
// Controller can transmit within the current periodic advertising interval
// for the advertising set, all the data shall be discarded and the
// Controller shall return the error code Packet Too Long (0x45).
if (advertiser.periodic_advertising_data.size() >
ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
advertiser.periodic_advertising_interval)) {
INFO(id_,
"the length of the combined periodic advertising data exceeds"
" the maximum that the controller can transmit within the current"
" periodic advertising interval");
advertiser.periodic_advertising_data.clear();
advertiser.partial_periodic_advertising_data = false;
return ErrorCode::PACKET_TOO_LONG;
}
return ErrorCode::SUCCESS;
}
// HCI LE Set Periodic Advertising Enable command (Vol 4, Part E § 7.8.63).
ErrorCode LinkLayerController::LeSetPeriodicAdvertisingEnable(
bool enable, bool include_adi, uint8_t advertising_handle) {
// If the advertising set corresponding to the Advertising_Handle parameter
// does not exist, the Controller shall return the error code Unknown
// Advertising Identifier (0x42).
// TODO(c++20) unordered_map<>::contains
if (extended_advertisers_.count(advertising_handle) == 0) {
INFO(id_, "no advertising set defined with handle {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER;
}
ExtendedAdvertiser& advertiser = extended_advertisers_[advertising_handle];
if (!enable) {
advertiser.DisablePeriodic();
return ErrorCode::SUCCESS;
}
// If bit 0 of Enable is set to 1 (periodic advertising is enabled) and the
// advertising set contains partial periodic advertising data, the Controller
// shall return the error code Command Disallowed (0x0C).
if (advertiser.partial_periodic_advertising_data) {
INFO(id_, "the advertising set contains partial periodic advertising data");
return ErrorCode::COMMAND_DISALLOWED;
}
// If bit 0 of Enable is set to 1 and the Host has not issued the
// HCI_LE_Set_Periodic_Advertising_Parameters command for the advertising set,
// the Controller shall either use vendor-specified parameters or return the
// error code Command Disallowed (0x0C).
if (advertiser.periodic_advertising_interval.count() == 0) {
INFO(id_, "periodic advertising is not configured for the set {:02x}",
static_cast<int>(advertising_handle));
return ErrorCode::COMMAND_DISALLOWED;
}
// If bit 0 of Enable is set to 1 and the length of the periodic advertising
// data is greater than the maximum that the Controller can transmit within
// the chosen periodic advertising interval, the Controller shall return the
// error code Packet Too Long (0x45).
if (advertiser.periodic_advertising_data.size() >
ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
advertiser.periodic_advertising_interval)) {
INFO(id_,
"the length of the combined periodic advertising data exceeds"
" the maximum that the controller can transmit within the current"
" periodic advertising interval");
return ErrorCode::PACKET_TOO_LONG;
}
// If bit 0 of Enable is set to 1 and the advertising set identified by the
// Advertising_Handle specified scannable, connectable, legacy, or anonymous
// advertising, the Controller shall return the error code
// Command Disallowed (0x0C).
if (advertiser.advertising_event_properties.connectable_ ||
advertiser.advertising_event_properties.scannable_ ||
advertiser.advertising_event_properties.legacy_ ||
advertiser.advertising_event_properties.anonymous_) {
INFO(id_,
"the periodic advertising set {:02x} specifies scannable,"
" connectable, legacy or anonymous advertising",
static_cast<int>(advertising_handle));
return ErrorCode::COMMAND_DISALLOWED;
}
// If bit 1 of Enable is set to 1 and the Controller does not support the
// Periodic Advertising ADI Support feature, the Controller shall return an
// error which should use the error code Unsupported Feature or
// Parameter Value (0x11).
if (include_adi && !properties_.SupportsLLFeature(
LLFeaturesBits::PERIODIC_ADVERTISING_ADI_SUPPORT)) {
INFO(id_,
"include ADI is true but the controller does not support the"
" Periodic Advertising ADI Supported feature",
static_cast<int>(advertising_handle));
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
advertiser.EnablePeriodic();
return ErrorCode::SUCCESS;
}
uint16_t ExtendedAdvertiser::GetMaxPeriodicAdvertisingDataLength(
slots /*periodic_advertising_interval*/) {
// TODO: evaluate the maximum length of the advertising PDU that can
// be physically sent in the advertising interval.
return max_extended_advertising_pdu_size;
}
// =============================================================================
// Advertising Routines
// =============================================================================
void LinkLayerController::LeAdvertising() {
chrono::time_point now = std::chrono::steady_clock::now();
// Legacy Advertising Timeout
// Generate HCI Connection Complete or Enhanced HCI Connection Complete
// events with Advertising Timeout error code when the advertising
// type is ADV_DIRECT_IND and the connection failed to be established.
if (legacy_advertiser_.IsEnabled() && legacy_advertiser_.timeout &&
now >= legacy_advertiser_.timeout.value()) {
// If the Advertising_Type parameter is 0x01 (ADV_DIRECT_IND, high duty
// cycle) and the directed advertising fails to create a connection, an
// HCI_LE_Connection_Complete or HCI_LE_Enhanced_Connection_Complete
// event shall be generated with the Status code set to
// Advertising Timeout (0x3C).
INFO(id_, "Directed Advertising Timeout");
legacy_advertiser_.Disable();
// TODO: The PTS tool expects an LE_Connection_Complete event in this
// case and will fail the test GAP/DISC/GENP/BV-05-C if
// LE_Enhanced_Connection_Complete is sent instead.
//
// Note: HCI_LE_Connection_Complete is not sent if the
// HCI_LE_Enhanced_Connection_Complete event (see Section 7.7.65.10)
// is unmasked.
#if 0
if (IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create(
ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
AddressType::PUBLIC_DEVICE_ADDRESS, Address(), Address(), Address(),
0, 0, 0, ClockAccuracy::PPM_500));
} else
#endif
if (IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create(
ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
AddressType::PUBLIC_DEVICE_ADDRESS, Address(), 0, 0, 0,
ClockAccuracy::PPM_500));
}
}
// Legacy Advertising Event
// Generate Link Layer Advertising events when advertising is enabled
// and a full interval has passed since the last event.
if (legacy_advertiser_.IsEnabled() && now >= legacy_advertiser_.next_event) {
legacy_advertiser_.next_event += legacy_advertiser_.advertising_interval;
model::packets::LegacyAdvertisingType type;
bool attach_advertising_data = true;
switch (legacy_advertiser_.advertising_type) {
case AdvertisingType::ADV_IND:
type = model::packets::LegacyAdvertisingType::ADV_IND;
break;
case AdvertisingType::ADV_DIRECT_IND_HIGH:
case AdvertisingType::ADV_DIRECT_IND_LOW:
attach_advertising_data = false;
type = model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
break;
case AdvertisingType::ADV_SCAN_IND:
type = model::packets::LegacyAdvertisingType::ADV_SCAN_IND;
break;
case AdvertisingType::ADV_NONCONN_IND:
type = model::packets::LegacyAdvertisingType::ADV_NONCONN_IND;
break;
}
SendLeLinkLayerPacket(
model::packets::LeLegacyAdvertisingPduBuilder::Create(
legacy_advertiser_.advertising_address.GetAddress(),
legacy_advertiser_.target_address.GetAddress(),
static_cast<model::packets::AddressType>(
legacy_advertiser_.advertising_address.GetAddressType()),
static_cast<model::packets::AddressType>(
legacy_advertiser_.target_address.GetAddressType()),
type,
attach_advertising_data ? legacy_advertiser_.advertising_data
: std::vector<uint8_t>{}),
properties_.le_advertising_physical_channel_tx_power);
}
for (auto& [_, advertiser] : extended_advertisers_) {
// Extended Advertising Timeouts
if (advertiser.IsEnabled() && advertiser.timeout.has_value() &&
now >= advertiser.timeout.value()) {
// If the Duration[i] parameter is set to a value other than 0x0000, an
// HCI_LE_Advertising_Set_Terminated event shall be generated when the
// duration specified in the Duration[i] parameter expires.
// However, if the advertising set is for high duty cycle connectable
// directed advertising and no connection is created before the duration
// expires, an HCI_LE_Connection_Complete or
// HCI_LE_Enhanced_Connection_Complete event with the Status parameter
// set to the error code Advertising Timeout (0x3C) may be generated
// instead of or in addition to the HCI_LE_Advertising_Set_Terminated
// event.
INFO(id_, "Extended Advertising Timeout");
advertiser.Disable();
bool high_duty_cycle_connectable_directed_advertising =
advertiser.advertising_event_properties.directed_ &&
advertiser.advertising_event_properties.connectable_ &&
advertiser.advertising_event_properties.high_duty_cycle_;
// Note: HCI_LE_Connection_Complete is not sent if the
// HCI_LE_Enhanced_Connection_Complete event (see Section 7.7.65.10)
// is unmasked.
if (high_duty_cycle_connectable_directed_advertising &&
IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create(
ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
AddressType::PUBLIC_DEVICE_ADDRESS, Address(), Address(), Address(),
0, 0, 0, ClockAccuracy::PPM_500));
} else if (high_duty_cycle_connectable_directed_advertising &&
IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create(
ErrorCode::ADVERTISING_TIMEOUT, 0, Role::CENTRAL,
AddressType::PUBLIC_DEVICE_ADDRESS, Address(), 0, 0, 0,
ClockAccuracy::PPM_500));
}
if (IsLeEventUnmasked(SubeventCode::ADVERTISING_SET_TERMINATED)) {
// The parameter Num_Completed_Extended_Advertising_Events is set
// only when Max_Extended_Advertising_Events was configured as
// non-zero in the advertising parameters.
uint8_t num_completed_extended_advertising_events =
advertiser.max_extended_advertising_events != 0
? advertiser.num_completed_extended_advertising_events
: 0;
send_event_(bluetooth::hci::LeAdvertisingSetTerminatedBuilder::Create(
ErrorCode::ADVERTISING_TIMEOUT, advertiser.advertising_handle, 0,
num_completed_extended_advertising_events));
}
}
if (advertiser.IsEnabled() && advertiser.max_extended_advertising_events &&
advertiser.num_completed_extended_advertising_events >=
advertiser.max_extended_advertising_events) {
// If the Max_Extended_Advertising_Events[i] parameter is set to a value
// other than 0x00, an HCI_LE_Advertising_Set_Terminated event shall be
// generated when the maximum number of extended advertising events has
// been transmitted by the Controller.
INFO(id_, "Max Extended Advertising count reached");
advertiser.Disable();
if (IsLeEventUnmasked(SubeventCode::ADVERTISING_SET_TERMINATED)) {
send_event_(bluetooth::hci::LeAdvertisingSetTerminatedBuilder::Create(
ErrorCode::ADVERTISING_TIMEOUT, advertiser.advertising_handle, 0,
advertiser.num_completed_extended_advertising_events));
}
}
// Extended Advertising Event
// Generate Link Layer Advertising events when advertising is enabled
// and a full interval has passed since the last event.
if (advertiser.IsEnabled() && now >= advertiser.next_event) {
advertiser.next_event += advertiser.primary_advertising_interval;
advertiser.num_completed_extended_advertising_events++;
if (advertiser.advertising_event_properties.legacy_) {
model::packets::LegacyAdvertisingType type;
uint16_t raw_advertising_event_properties =
ExtendedAdvertiser::GetRawAdvertisingEventProperties(
advertiser.advertising_event_properties);
switch (static_cast<LegacyAdvertisingEventProperties>(
raw_advertising_event_properties & 0xf)) {
case LegacyAdvertisingEventProperties::ADV_IND:
type = model::packets::LegacyAdvertisingType::ADV_IND;
break;
case LegacyAdvertisingEventProperties::ADV_DIRECT_IND_HIGH:
case LegacyAdvertisingEventProperties::ADV_DIRECT_IND_LOW:
type = model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
break;
case LegacyAdvertisingEventProperties::ADV_SCAN_IND:
type = model::packets::LegacyAdvertisingType::ADV_SCAN_IND;
break;
case LegacyAdvertisingEventProperties::ADV_NONCONN_IND:
type = model::packets::LegacyAdvertisingType::ADV_NONCONN_IND;
break;
default:
FATAL(
id_,
"unexpected raw advertising event properties;"
" please check the extended advertising parameter validation");
break;
}
SendLeLinkLayerPacket(
model::packets::LeLegacyAdvertisingPduBuilder::Create(
advertiser.advertising_address.GetAddress(),
advertiser.target_address.GetAddress(),
static_cast<model::packets::AddressType>(
advertiser.advertising_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertiser.target_address.GetAddressType()),
type, advertiser.advertising_data),
advertiser.advertising_tx_power);
} else {
SendLeLinkLayerPacket(
model::packets::LeExtendedAdvertisingPduBuilder::Create(
advertiser.advertising_address.GetAddress(),
advertiser.target_address.GetAddress(),
static_cast<model::packets::AddressType>(
advertiser.advertising_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertiser.target_address.GetAddressType()),
advertiser.advertising_event_properties.connectable_,
advertiser.advertising_event_properties.scannable_,
advertiser.advertising_event_properties.directed_,
advertiser.advertising_sid, advertiser.advertising_tx_power,
static_cast<model::packets::PrimaryPhyType>(
advertiser.primary_advertising_phy),
static_cast<model::packets::SecondaryPhyType>(
advertiser.secondary_advertising_phy),
advertiser.periodic_advertising_interval.count(),
advertiser.advertising_data),
advertiser.advertising_tx_power);
}
}
// Periodic Advertising Event
// Generate Link Layer Advertising events when advertising is enabled
// and a full interval has passed since the last event.
if (advertiser.IsPeriodicEnabled() &&
now >= advertiser.next_periodic_event) {
advertiser.next_periodic_event +=
advertiser.periodic_advertising_interval;
SendLeLinkLayerPacket(
model::packets::LePeriodicAdvertisingPduBuilder::Create(
advertiser.advertising_address.GetAddress(), Address(),
static_cast<model::packets::AddressType>(
advertiser.advertising_address.GetAddressType()),
advertiser.advertising_sid, advertiser.advertising_tx_power,
advertiser.periodic_advertising_interval.count(),
advertiser.periodic_advertising_data),
advertiser.advertising_tx_power);
}
}
}
} // namespace rootcanal