| /* |
| * 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 |