/* * 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. // NB: The interval is specified to be 3.75ms, but it does not make sense to // use this value with the timer tick at 5ms. const chrono::duration adv_direct_ind_high_timeout = 1280ms; const chrono::duration adv_direct_ind_high_interval = 10ms /*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鈥檚 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鈥檚 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; } // If an IRK is available in the Link Layer Resolving List for the peer // device, then the target鈥檚 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鈥檚 device address (TargetA field) shall use the Identity // Address when entering the Advertising State and using connectable // directed events. if (advertiser.IsDirected()) { advertiser.target_address = GenerateResolvablePrivateAddress(peer_address, IrkSelection::Peer) .value_or(peer_address); } } 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 = now + 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::PhyType>( advertiser.primary_advertising_phy), static_cast<model::packets::PhyType>( 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