| /* |
| * Copyright 2017 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 "link_layer_controller.h" |
| |
| #include <algorithm> |
| #include <hci/hci_packets.h> |
| #include <lmp.h> |
| |
| #include "crypto/crypto.h" |
| #include "log.h" |
| #include "packet/raw_builder.h" |
| |
| using namespace std::chrono; |
| using bluetooth::hci::Address; |
| using bluetooth::hci::AddressType; |
| using bluetooth::hci::AddressWithType; |
| using bluetooth::hci::DirectAdvertisingAddressType; |
| using bluetooth::hci::EventCode; |
| using bluetooth::hci::LLFeaturesBits; |
| using bluetooth::hci::SubeventCode; |
| |
| using namespace model::packets; |
| using model::packets::PacketType; |
| using namespace std::literals; |
| |
| using TaskId = rootcanal::LinkLayerController::TaskId; |
| |
| // Temporay define, to be replaced when verbose log level is implemented. |
| #define LOG_VERB(...) LOG_INFO(__VA_ARGS__) |
| |
| namespace rootcanal { |
| |
| constexpr milliseconds kNoDelayMs(0); |
| |
| const Address& LinkLayerController::GetAddress() const { return address_; } |
| |
| AddressWithType PeerDeviceAddress(Address address, |
| PeerAddressType peer_address_type) { |
| switch (peer_address_type) { |
| case PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS: |
| return AddressWithType(address, AddressType::PUBLIC_DEVICE_ADDRESS); |
| case PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS: |
| return AddressWithType(address, AddressType::RANDOM_DEVICE_ADDRESS); |
| } |
| } |
| |
| AddressWithType PeerIdentityAddress(Address address, |
| PeerAddressType peer_address_type) { |
| switch (peer_address_type) { |
| case PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS: |
| return AddressWithType(address, AddressType::PUBLIC_IDENTITY_ADDRESS); |
| case PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS: |
| return AddressWithType(address, AddressType::RANDOM_IDENTITY_ADDRESS); |
| } |
| } |
| |
| bool LinkLayerController::IsEventUnmasked(EventCode event) const { |
| uint64_t bit = UINT64_C(1) << (static_cast<uint8_t>(event) - 1); |
| return (event_mask_ & bit) != 0; |
| } |
| |
| bool LinkLayerController::IsLeEventUnmasked(SubeventCode subevent) const { |
| uint64_t bit = UINT64_C(1) << (static_cast<uint8_t>(subevent) - 1); |
| return IsEventUnmasked(EventCode::LE_META_EVENT) && |
| (le_event_mask_ & bit) != 0; |
| } |
| |
| bool LinkLayerController::FilterAcceptListBusy() { |
| // Filter Accept List cannot be modified when |
| // • any advertising filter policy uses the Filter Accept List and |
| // advertising is enabled, |
| if (legacy_advertiser_.IsEnabled() && |
| legacy_advertiser_.advertising_filter_policy != |
| bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES) { |
| return true; |
| } |
| |
| for (auto const& [_, advertiser] : extended_advertisers_) { |
| if (advertiser.IsEnabled() && |
| advertiser.advertising_filter_policy != |
| bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES) { |
| return true; |
| } |
| } |
| |
| // • the scanning filter policy uses the Filter Accept List and scanning |
| // is enabled, |
| if (scanner_.IsEnabled() && |
| (scanner_.scan_filter_policy == |
| bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY || |
| scanner_.scan_filter_policy == |
| bluetooth::hci::LeScanningFilterPolicy:: |
| FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY)) { |
| return true; |
| } |
| |
| // • the initiator filter policy uses the Filter Accept List and an |
| // HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection |
| // command is pending. |
| if (initiator_.IsEnabled() && |
| initiator_.initiator_filter_policy == |
| bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool LinkLayerController::LeFilterAcceptListContainsDevice( |
| FilterAcceptListAddressType address_type, Address address) { |
| for (auto const& entry : le_filter_accept_list_) { |
| if (entry.address_type == address_type && |
| (address_type == FilterAcceptListAddressType::ANONYMOUS_ADVERTISERS || |
| entry.address == address)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool LinkLayerController::LePeriodicAdvertiserListContainsDevice( |
| bluetooth::hci::AdvertiserAddressType advertiser_address_type, |
| Address advertiser_address, uint8_t advertising_sid) { |
| for (auto const& entry : le_periodic_advertiser_list_) { |
| if (entry.advertiser_address_type == advertiser_address_type && |
| entry.advertiser_address == advertiser_address && |
| entry.advertising_sid == advertising_sid) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool LinkLayerController::LeFilterAcceptListContainsDevice( |
| AddressWithType address) { |
| FilterAcceptListAddressType address_type; |
| switch (address.GetAddressType()) { |
| case AddressType::PUBLIC_DEVICE_ADDRESS: |
| case AddressType::PUBLIC_IDENTITY_ADDRESS: |
| address_type = FilterAcceptListAddressType::PUBLIC; |
| break; |
| case AddressType::RANDOM_DEVICE_ADDRESS: |
| case AddressType::RANDOM_IDENTITY_ADDRESS: |
| address_type = FilterAcceptListAddressType::RANDOM; |
| break; |
| } |
| |
| return LeFilterAcceptListContainsDevice(address_type, address.GetAddress()); |
| } |
| |
| bool LinkLayerController::ResolvingListBusy() { |
| // The resolving list cannot be modified when |
| // • Advertising (other than periodic advertising) is enabled, |
| if (legacy_advertiser_.IsEnabled()) { |
| return true; |
| } |
| |
| for (auto const& [_, advertiser] : extended_advertisers_) { |
| if (advertiser.IsEnabled()) { |
| return true; |
| } |
| } |
| |
| // • Scanning is enabled, |
| if (scanner_.IsEnabled()) { |
| return true; |
| } |
| |
| // • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or |
| // HCI_LE_Periodic_Advertising_Create_Sync command is pending. |
| if (initiator_.IsEnabled()) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| std::optional<AddressWithType> LinkLayerController::ResolvePrivateAddress( |
| AddressWithType address, IrkSelection irk) { |
| if (!address.IsRpa()) { |
| return address; |
| } |
| |
| if (!le_resolving_list_enabled_) { |
| return {}; |
| } |
| |
| for (auto& entry : le_resolving_list_) { |
| std::array<uint8_t, LinkLayerController::kIrkSize> const& used_irk = |
| irk == IrkSelection::Local ? entry.local_irk : entry.peer_irk; |
| |
| if (address.IsRpaThatMatchesIrk(used_irk)) { |
| // Update the peer resolvable address used for the peer |
| // with the returned identity address. |
| if (irk == IrkSelection::Peer) { |
| entry.peer_resolvable_address = address.GetAddress(); |
| } |
| |
| return PeerIdentityAddress(entry.peer_identity_address, |
| entry.peer_identity_address_type); |
| } |
| } |
| |
| return {}; |
| } |
| |
| std::optional<AddressWithType> |
| LinkLayerController::GenerateResolvablePrivateAddress(AddressWithType address, |
| IrkSelection irk) { |
| if (!le_resolving_list_enabled_) { |
| return {}; |
| } |
| |
| for (auto& entry : le_resolving_list_) { |
| if (address.GetAddress() == entry.peer_identity_address && |
| address.ToPeerAddressType() == entry.peer_identity_address_type) { |
| std::array<uint8_t, LinkLayerController::kIrkSize> const& used_irk = |
| irk == IrkSelection::Local ? entry.local_irk : entry.peer_irk; |
| Address local_resolvable_address = generate_rpa(used_irk); |
| |
| // Update the local resolvable address used for the peer |
| // with the returned identity address. |
| if (irk == IrkSelection::Local) { |
| entry.local_resolvable_address = local_resolvable_address; |
| } |
| |
| return AddressWithType{local_resolvable_address, |
| AddressType::RANDOM_DEVICE_ADDRESS}; |
| } |
| } |
| |
| return {}; |
| } |
| |
| // ============================================================================= |
| // BR/EDR Commands |
| // ============================================================================= |
| |
| // HCI Read Rssi command (Vol 4, Part E § 7.5.4). |
| ErrorCode LinkLayerController::ReadRssi(uint16_t connection_handle, |
| int8_t* rssi) { |
| // Not documented: If the connection handle is not found, the Controller |
| // shall return the error code Unknown Connection Identifier (0x02). |
| if (!connections_.HasHandle(connection_handle)) { |
| LOG_INFO("unknown connection identifier"); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| *rssi = connections_.GetRssi(connection_handle); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // General LE Commands |
| // ============================================================================= |
| |
| // HCI LE Set Random Address command (Vol 4, Part E § 7.8.4). |
| ErrorCode LinkLayerController::LeSetRandomAddress(Address random_address) { |
| // If the Host issues this command when any of advertising (created using |
| // legacy advertising commands), scanning, or initiating are enabled, |
| // the Controller shall return the error code Command Disallowed (0x0C). |
| if (legacy_advertiser_.IsEnabled() || scanner_.IsEnabled() || |
| initiator_.IsEnabled()) { |
| LOG_INFO("advertising, scanning or initiating are currently active"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| if (random_address == Address::kEmpty) { |
| LOG_INFO("the random address may not be set to 00:00:00:00:00:00"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| LOG_INFO("device random address configured to %s", |
| random_address.ToString().c_str()); |
| random_address_ = random_address; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Set Host Feature command (Vol 4, Part E § 7.8.45). |
| ErrorCode LinkLayerController::LeSetResolvablePrivateAddressTimeout( |
| uint16_t rpa_timeout) { |
| // Note: no documented status code for this case. |
| if (rpa_timeout < 0x1 || rpa_timeout > 0x0e10) { |
| LOG_INFO( |
| "rpa_timeout (0x%04x) is outside the range of supported values " |
| " 0x1 - 0x0e10", |
| rpa_timeout); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| resolvable_private_address_timeout_ = seconds(rpa_timeout); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Read Phy command (Vol 4, Part E § 7.8.47). |
| ErrorCode LinkLayerController::LeReadPhy(uint16_t connection_handle, |
| bluetooth::hci::PhyType* tx_phy, |
| bluetooth::hci::PhyType* rx_phy) { |
| // Note: no documented status code for this case. |
| if (!connections_.HasHandle(connection_handle) || |
| connections_.GetPhyType(connection_handle) != Phy::Type::LOW_ENERGY) { |
| LOG_INFO("unknown or invalid connection handle"); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| AclConnection const& connection = |
| connections_.GetAclConnection(connection_handle); |
| *tx_phy = connection.GetTxPhy(); |
| *rx_phy = connection.GetRxPhy(); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Set Default Phy command (Vol 4, Part E § 7.8.48). |
| ErrorCode LinkLayerController::LeSetDefaultPhy( |
| bool all_phys_no_transmit_preference, bool all_phys_no_receive_preference, |
| uint8_t tx_phys, uint8_t rx_phys) { |
| uint8_t supported_phys = properties_.LeSupportedPhys(); |
| |
| // If the All_PHYs parameter specifies that the Host has no preference, |
| // the TX_PHYs parameter shall be ignored; otherwise at least one bit shall |
| // be set to 1. |
| if (all_phys_no_transmit_preference) { |
| tx_phys = supported_phys; |
| } |
| if (tx_phys == 0) { |
| LOG_INFO("TX_Phys does not configure any bit"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If the All_PHYs parameter specifies that the Host has no preference, |
| // the RX_PHYs parameter shall be ignored; otherwise at least one bit shall |
| // be set to 1. |
| if (all_phys_no_receive_preference) { |
| rx_phys = supported_phys; |
| } |
| if (rx_phys == 0) { |
| LOG_INFO("RX_Phys does not configure any bit"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If the Host sets, in the TX_PHYs or RX_PHYs parameter, a bit for a PHY that |
| // the Controller does not support, including a bit that is reserved for |
| // future use, the Controller shall return the error code Unsupported Feature |
| // or Parameter Value (0x11). |
| if ((tx_phys & ~supported_phys) != 0) { |
| LOG_INFO("TX_PhyS (%x) configures unsupported or reserved bits", tx_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| if ((rx_phys & ~supported_phys) != 0) { |
| LOG_INFO("RX_PhyS (%x) configures unsupported or reserved bits", rx_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| default_tx_phys_ = tx_phys; |
| default_rx_phys_ = rx_phys; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Set Phy command (Vol 4, Part E § 7.8.49). |
| ErrorCode LinkLayerController::LeSetPhy( |
| uint16_t connection_handle, bool all_phys_no_transmit_preference, |
| bool all_phys_no_receive_preference, uint8_t tx_phys, uint8_t rx_phys, |
| bluetooth::hci::PhyOptions /*phy_options*/) { |
| uint8_t supported_phys = properties_.LeSupportedPhys(); |
| |
| // Note: no documented status code for this case. |
| if (!connections_.HasHandle(connection_handle) || |
| connections_.GetPhyType(connection_handle) != Phy::Type::LOW_ENERGY) { |
| LOG_INFO("unknown or invalid connection handle"); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // If the All_PHYs parameter specifies that the Host has no preference, |
| // the TX_PHYs parameter shall be ignored; otherwise at least one bit shall |
| // be set to 1. |
| if (all_phys_no_transmit_preference) { |
| tx_phys = supported_phys; |
| } |
| if (tx_phys == 0) { |
| LOG_INFO("TX_Phys does not configure any bit"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If the All_PHYs parameter specifies that the Host has no preference, |
| // the RX_PHYs parameter shall be ignored; otherwise at least one bit shall |
| // be set to 1. |
| if (all_phys_no_receive_preference) { |
| rx_phys = supported_phys; |
| } |
| if (rx_phys == 0) { |
| LOG_INFO("RX_Phys does not configure any bit"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If the Host sets, in the TX_PHYs or RX_PHYs parameter, a bit for a PHY that |
| // the Controller does not support, including a bit that is reserved for |
| // future use, the Controller shall return the error code Unsupported Feature |
| // or Parameter Value (0x11). |
| if ((tx_phys & ~supported_phys) != 0) { |
| LOG_INFO("TX_PhyS (%x) configures unsupported or reserved bits", tx_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| if ((rx_phys & ~supported_phys) != 0) { |
| LOG_INFO("RX_PhyS (%x) configures unsupported or reserved bits", rx_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The HCI_LE_PHY_Update_Complete event shall be generated either when one |
| // or both PHY changes or when the Controller determines that neither PHY |
| // will change immediately. |
| SendLeLinkLayerPacket(model::packets::LlPhyReqBuilder::Create( |
| connections_.GetOwnAddress(connection_handle).GetAddress(), |
| connections_.GetAddress(connection_handle).GetAddress(), tx_phys, |
| rx_phys)); |
| |
| connections_.GetAclConnection(connection_handle).InitiatePhyUpdate(); |
| requested_tx_phys_ = tx_phys; |
| requested_rx_phys_ = rx_phys; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // Helper to pick one phy in enabled phys. |
| static bluetooth::hci::PhyType select_phy(uint8_t phys, |
| bluetooth::hci::PhyType current) { |
| return (phys & 0x4) ? bluetooth::hci::PhyType::LE_CODED |
| : (phys & 0x2) ? bluetooth::hci::PhyType::LE_2M |
| : (phys & 0x1) ? bluetooth::hci::PhyType::LE_1M |
| : current; |
| } |
| |
| // Helper to generate the LL_PHY_UPDATE_IND mask for the selected phy. |
| // The mask is non zero only if the phy has changed. |
| static uint8_t indicate_phy(bluetooth::hci::PhyType selected, |
| bluetooth::hci::PhyType current) { |
| return selected == current ? 0x0 |
| : selected == bluetooth::hci::PhyType::LE_CODED ? 0x4 |
| : selected == bluetooth::hci::PhyType::LE_2M ? 0x2 |
| : 0x1; |
| } |
| |
| void LinkLayerController::IncomingLlPhyReq( |
| model::packets::LinkLayerPacketView incoming) { |
| auto phy_req = model::packets::LlPhyReqView::Create(incoming); |
| ASSERT(phy_req.IsValid()); |
| uint16_t connection_handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| |
| if (connection_handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| incoming.GetSourceAddress().ToString().c_str()); |
| return; |
| } |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| |
| if (connection.GetRole() == bluetooth::hci::Role::PERIPHERAL) { |
| // Peripheral receives the request: respond with local phy preferences |
| // in LL_PHY_RSP pdu. |
| SendLeLinkLayerPacket(model::packets::LlPhyRspBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| default_tx_phys_, default_rx_phys_)); |
| } else { |
| // Central receives the request: respond with LL_PHY_UPDATE_IND and |
| // the selected phys. |
| |
| // Intersect phy preferences with local preferences. |
| uint8_t tx_phys = phy_req.GetRxPhys() & default_tx_phys_; |
| uint8_t rx_phys = phy_req.GetTxPhys() & default_rx_phys_; |
| |
| // Select valid TX and RX phys from preferences. |
| bluetooth::hci::PhyType phy_c_to_p = |
| select_phy(tx_phys, connection.GetTxPhy()); |
| bluetooth::hci::PhyType phy_p_to_c = |
| select_phy(rx_phys, connection.GetRxPhy()); |
| |
| // Send LL_PHY_UPDATE_IND to notify selected phys. |
| // |
| // PHY_C_TO_P shall be set to indicate the PHY that shall be used for |
| // packets sent from the Central to the Peripheral. These fields each |
| // consist of 8 bits. If a PHY is changing, the bit corresponding to the new |
| // PHY shall be set to 1 and the remaining bits to 0; if a PHY is remaining |
| // unchanged, then the corresponding field shall be set to the value 0. |
| SendLeLinkLayerPacket(model::packets::LlPhyUpdateIndBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| indicate_phy(phy_c_to_p, connection.GetTxPhy()), |
| indicate_phy(phy_p_to_c, connection.GetRxPhy()), 0)); |
| |
| // Notify the host when the phy selection has changed |
| // (responder in this case). |
| if ((phy_c_to_p != connection.GetTxPhy() || |
| phy_p_to_c != connection.GetRxPhy()) && |
| IsLeEventUnmasked(SubeventCode::PHY_UPDATE_COMPLETE)) { |
| send_event_(bluetooth::hci::LePhyUpdateCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connection_handle, phy_c_to_p, phy_p_to_c)); |
| } |
| |
| // Update local state. |
| connection.SetTxPhy(phy_c_to_p); |
| connection.SetRxPhy(phy_p_to_c); |
| } |
| } |
| |
| void LinkLayerController::IncomingLlPhyRsp( |
| model::packets::LinkLayerPacketView incoming) { |
| auto phy_rsp = model::packets::LlPhyRspView::Create(incoming); |
| ASSERT(phy_rsp.IsValid()); |
| uint16_t connection_handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| |
| if (connection_handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| incoming.GetSourceAddress().ToString().c_str()); |
| return; |
| } |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| ASSERT(connection.GetRole() == bluetooth::hci::Role::CENTRAL); |
| |
| // Intersect phy preferences with local preferences. |
| uint8_t tx_phys = phy_rsp.GetRxPhys() & requested_tx_phys_; |
| uint8_t rx_phys = phy_rsp.GetTxPhys() & requested_rx_phys_; |
| |
| // Select valid TX and RX phys from preferences. |
| bluetooth::hci::PhyType phy_c_to_p = |
| select_phy(tx_phys, connection.GetTxPhy()); |
| bluetooth::hci::PhyType phy_p_to_c = |
| select_phy(rx_phys, connection.GetRxPhy()); |
| |
| // Send LL_PHY_UPDATE_IND to notify selected phys. |
| // |
| // PHY_C_TO_P shall be set to indicate the PHY that shall be used for |
| // packets sent from the Central to the Peripheral. These fields each |
| // consist of 8 bits. If a PHY is changing, the bit corresponding to the new |
| // PHY shall be set to 1 and the remaining bits to 0; if a PHY is remaining |
| // unchanged, then the corresponding field shall be set to the value 0. |
| SendLeLinkLayerPacket(model::packets::LlPhyUpdateIndBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| indicate_phy(phy_c_to_p, connection.GetTxPhy()), |
| indicate_phy(phy_p_to_c, connection.GetRxPhy()), 0)); |
| |
| // Always notify the host, even if the phy selection has not changed |
| // (initiator in this case). |
| if (IsLeEventUnmasked(SubeventCode::PHY_UPDATE_COMPLETE)) { |
| send_event_(bluetooth::hci::LePhyUpdateCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connection_handle, phy_c_to_p, phy_p_to_c)); |
| } |
| |
| // Update local state. |
| connection.PhyUpdateComplete(); |
| connection.SetTxPhy(phy_c_to_p); |
| connection.SetRxPhy(phy_p_to_c); |
| } |
| |
| void LinkLayerController::IncomingLlPhyUpdateInd( |
| model::packets::LinkLayerPacketView incoming) { |
| auto phy_update_ind = model::packets::LlPhyUpdateIndView::Create(incoming); |
| ASSERT(phy_update_ind.IsValid()); |
| uint16_t connection_handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| |
| if (connection_handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| incoming.GetSourceAddress().ToString().c_str()); |
| return; |
| } |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| ASSERT(connection.GetRole() == bluetooth::hci::Role::PERIPHERAL); |
| |
| bluetooth::hci::PhyType tx_phy = |
| select_phy(phy_update_ind.GetPhyPToC(), connection.GetTxPhy()); |
| bluetooth::hci::PhyType rx_phy = |
| select_phy(phy_update_ind.GetPhyCToP(), connection.GetRxPhy()); |
| |
| // Update local state, and notify the host. |
| // The notification is sent only when the local host is initiator |
| // of the Phy update procedure or the phy selection has changed. |
| if (IsLeEventUnmasked(SubeventCode::PHY_UPDATE_COMPLETE) && |
| (tx_phy != connection.GetTxPhy() || rx_phy != connection.GetRxPhy() || |
| connection.InitiatedPhyUpdate())) { |
| send_event_(bluetooth::hci::LePhyUpdateCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connection_handle, tx_phy, rx_phy)); |
| } |
| |
| connection.PhyUpdateComplete(); |
| connection.SetTxPhy(tx_phy); |
| connection.SetRxPhy(rx_phy); |
| } |
| |
| // HCI LE Set Host Feature command (Vol 4, Part E § 7.8.115). |
| ErrorCode LinkLayerController::LeSetHostFeature(uint8_t bit_number, |
| uint8_t bit_value) { |
| if (bit_number >= 64 || bit_value > 1) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If Bit_Value is set to 0x01 and Bit_Number specifies a feature bit that |
| // requires support of a feature that the Controller does not support, |
| // the Controller shall return the error code Unsupported Feature or |
| // Parameter Value (0x11). |
| // TODO |
| |
| // If the Host issues this command while the Controller has a connection to |
| // another device, the Controller shall return the error code |
| // Command Disallowed (0x0C). |
| if (HasAclConnection()) { |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| uint64_t bit_mask = UINT64_C(1) << bit_number; |
| if (bit_mask == |
| static_cast<uint64_t>( |
| LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_HOST_SUPPORT)) { |
| connected_isochronous_stream_host_support_ = bit_value != 0; |
| } else if (bit_mask == |
| static_cast<uint64_t>( |
| LLFeaturesBits::CONNECTION_SUBRATING_HOST_SUPPORT)) { |
| connection_subrating_host_support_ = bit_value != 0; |
| } |
| // If Bit_Number specifies a feature bit that is not controlled by the Host, |
| // the Controller shall return the error code Unsupported Feature or |
| // Parameter Value (0x11). |
| else { |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| if (bit_value != 0) { |
| le_host_supported_features_ |= bit_mask; |
| } else { |
| le_host_supported_features_ &= ~bit_mask; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // LE Resolving List |
| // ============================================================================= |
| |
| // HCI command LE_Add_Device_To_Resolving_List (Vol 4, Part E § 7.8.38). |
| ErrorCode LinkLayerController::LeAddDeviceToResolvingList( |
| PeerAddressType peer_identity_address_type, Address peer_identity_address, |
| std::array<uint8_t, kIrkSize> peer_irk, |
| std::array<uint8_t, kIrkSize> local_irk) { |
| // This command shall not be used when address resolution is enabled in the |
| // Controller and: |
| // • Advertising (other than periodic advertising) is enabled, |
| // • Scanning is enabled, or |
| // • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or |
| // HCI_LE_Periodic_Advertising_Create_Sync command is pending. |
| if (le_resolving_list_enabled_ && ResolvingListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning, or establishing an" |
| " LE connection"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // When a Controller cannot add a device to the list because there is no space |
| // available, it shall return the error code Memory Capacity Exceeded (0x07). |
| if (le_resolving_list_.size() >= properties_.le_resolving_list_size) { |
| LOG_INFO("resolving list is full"); |
| return ErrorCode::MEMORY_CAPACITY_EXCEEDED; |
| } |
| |
| // If there is an existing entry in the resolving list with the same |
| // Peer_Identity_Address and Peer_Identity_Address_Type, or with the same |
| // Peer_IRK, the Controller should return the error code Invalid HCI Command |
| // Parameters (0x12). |
| for (auto const& entry : le_resolving_list_) { |
| if ((entry.peer_identity_address_type == peer_identity_address_type && |
| entry.peer_identity_address == peer_identity_address) || |
| entry.peer_irk == peer_irk) { |
| LOG_INFO("device is already present in the resolving list"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| } |
| |
| le_resolving_list_.emplace_back(ResolvingListEntry{peer_identity_address_type, |
| peer_identity_address, |
| peer_irk, |
| local_irk, |
| PrivacyMode::NETWORK, |
| {}, |
| {}}); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI command LE_Remove_Device_From_Resolving_List (Vol 4, Part E § 7.8.39). |
| ErrorCode LinkLayerController::LeRemoveDeviceFromResolvingList( |
| PeerAddressType peer_identity_address_type, Address peer_identity_address) { |
| // This command shall not be used when address resolution is enabled in the |
| // Controller and: |
| // • Advertising (other than periodic advertising) is enabled, |
| // • Scanning is enabled, or |
| // • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or |
| // HCI_LE_Periodic_Advertising_Create_Sync command is pending. |
| if (le_resolving_list_enabled_ && ResolvingListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning, or establishing an" |
| " LE connection"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| for (auto it = le_resolving_list_.begin(); it != le_resolving_list_.end(); |
| it++) { |
| if (it->peer_identity_address_type == peer_identity_address_type && |
| it->peer_identity_address == peer_identity_address) { |
| le_resolving_list_.erase(it); |
| return ErrorCode::SUCCESS; |
| } |
| } |
| |
| // When a Controller cannot remove a device from the resolving list because |
| // it is not found, it shall return the error code |
| // Unknown Connection Identifier (0x02). |
| LOG_INFO("peer address not found in the resolving list"); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // HCI command LE_Clear_Resolving_List (Vol 4, Part E § 7.8.40). |
| ErrorCode LinkLayerController::LeClearResolvingList() { |
| // This command shall not be used when address resolution is enabled in the |
| // Controller and: |
| // • Advertising (other than periodic advertising) is enabled, |
| // • Scanning is enabled, or |
| // • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or |
| // HCI_LE_Periodic_Advertising_Create_Sync command is pending. |
| if (le_resolving_list_enabled_ && ResolvingListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning," |
| " or establishing an LE connection"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| le_resolving_list_.clear(); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI command LE_Read_Peer_Resolvable_Address (Vol 4, Part E § 7.8.42). |
| ErrorCode LinkLayerController::LeReadPeerResolvableAddress( |
| PeerAddressType peer_identity_address_type, Address peer_identity_address, |
| Address* peer_resolvable_address) { |
| for (auto const& entry : le_resolving_list_) { |
| if (entry.peer_identity_address_type == peer_identity_address_type && |
| entry.peer_identity_address == peer_identity_address && |
| entry.peer_resolvable_address.has_value()) { |
| *peer_resolvable_address = entry.peer_resolvable_address.value(); |
| return ErrorCode::SUCCESS; |
| } |
| } |
| |
| // When a Controller cannot find a Resolvable Private Address associated with |
| // the Peer Identity Address, or if the Peer Identity Address cannot be found |
| // in the resolving list, it shall return the error code |
| // Unknown Connection Identifier (0x02). |
| LOG_INFO( |
| "peer identity address %s[%s] not found in the resolving list," |
| " or peer resolvable address unavailable", |
| peer_identity_address.ToString().c_str(), |
| PeerAddressTypeText(peer_identity_address_type).c_str()); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // HCI command LE_Read_Local_Resolvable_Address (Vol 4, Part E § 7.8.43). |
| ErrorCode LinkLayerController::LeReadLocalResolvableAddress( |
| PeerAddressType peer_identity_address_type, Address peer_identity_address, |
| Address* local_resolvable_address) { |
| for (auto const& entry : le_resolving_list_) { |
| if (entry.peer_identity_address_type == peer_identity_address_type && |
| entry.peer_identity_address == peer_identity_address && |
| entry.local_resolvable_address.has_value()) { |
| *local_resolvable_address = entry.local_resolvable_address.value(); |
| return ErrorCode::SUCCESS; |
| } |
| } |
| |
| // When a Controller cannot find a Resolvable Private Address associated with |
| // the Peer Identity Address, or if the Peer Identity Address cannot be found |
| // in the resolving list, it shall return the error code |
| // Unknown Connection Identifier (0x02). |
| LOG_INFO( |
| "peer identity address %s[%s] not found in the resolving list," |
| " or peer resolvable address unavailable", |
| peer_identity_address.ToString().c_str(), |
| PeerAddressTypeText(peer_identity_address_type).c_str()); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // HCI command LE_Set_Address_Resolution_Enable (Vol 4, Part E § 7.8.44). |
| ErrorCode LinkLayerController::LeSetAddressResolutionEnable(bool enable) { |
| // This command shall not be used when: |
| // • Advertising (other than periodic advertising) is enabled, |
| // • Scanning is enabled, or |
| // • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or |
| // HCI_LE_Periodic_Advertising_Create_Sync command is pending. |
| if (ResolvingListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning," |
| " or establishing an LE connection"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| le_resolving_list_enabled_ = enable; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI command LE_Set_Privacy_Mode (Vol 4, Part E § 7.8.77). |
| ErrorCode LinkLayerController::LeSetPrivacyMode( |
| PeerAddressType peer_identity_address_type, Address peer_identity_address, |
| bluetooth::hci::PrivacyMode privacy_mode) { |
| // This command shall not be used when address resolution is enabled in the |
| // Controller and: |
| // • Advertising (other than periodic advertising) is enabled, |
| // • Scanning is enabled, or |
| // • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or |
| // HCI_LE_Periodic_Advertising_Create_Sync command is pending. |
| if (le_resolving_list_enabled_ && ResolvingListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning," |
| " or establishing an LE connection"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| for (auto& entry : le_resolving_list_) { |
| if (entry.peer_identity_address_type == peer_identity_address_type && |
| entry.peer_identity_address == peer_identity_address) { |
| entry.privacy_mode = privacy_mode; |
| return ErrorCode::SUCCESS; |
| } |
| } |
| |
| // If the device is not on the resolving list, the Controller shall return |
| // the error code Unknown Connection Identifier (0x02). |
| LOG_INFO("peer address not found in the resolving list"); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // ============================================================================= |
| // LE Filter Accept List |
| // ============================================================================= |
| |
| // HCI command LE_Clear_Filter_Accept_List (Vol 4, Part E § 7.8.15). |
| ErrorCode LinkLayerController::LeClearFilterAcceptList() { |
| // This command shall not be used when: |
| // • any advertising filter policy uses the Filter Accept List and |
| // advertising is enabled, |
| // • the scanning filter policy uses the Filter Accept List and scanning |
| // is enabled, or |
| // • the initiator filter policy uses the Filter Accept List and an |
| // HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection |
| // command is pending. |
| if (FilterAcceptListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning," |
| " or establishing an LE connection using the filter accept list"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| le_filter_accept_list_.clear(); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI command LE_Add_Device_To_Filter_Accept_List (Vol 4, Part E § 7.8.16). |
| ErrorCode LinkLayerController::LeAddDeviceToFilterAcceptList( |
| FilterAcceptListAddressType address_type, Address address) { |
| // This command shall not be used when: |
| // • any advertising filter policy uses the Filter Accept List and |
| // advertising is enabled, |
| // • the scanning filter policy uses the Filter Accept List and scanning |
| // is enabled, or |
| // • the initiator filter policy uses the Filter Accept List and an |
| // HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection |
| // command is pending. |
| if (FilterAcceptListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning," |
| " or establishing an LE connection using the filter accept list"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // When a Controller cannot add a device to the Filter Accept List |
| // because there is no space available, it shall return the error code |
| // Memory Capacity Exceeded (0x07). |
| if (le_filter_accept_list_.size() >= properties_.le_filter_accept_list_size) { |
| LOG_INFO("filter accept list is full"); |
| return ErrorCode::MEMORY_CAPACITY_EXCEEDED; |
| } |
| |
| le_filter_accept_list_.emplace_back( |
| FilterAcceptListEntry{address_type, address}); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI command LE_Remove_Device_From_Filter_Accept_List (Vol 4, Part E |
| // § 7.8.17). |
| ErrorCode LinkLayerController::LeRemoveDeviceFromFilterAcceptList( |
| FilterAcceptListAddressType address_type, Address address) { |
| // This command shall not be used when: |
| // • any advertising filter policy uses the Filter Accept List and |
| // advertising is enabled, |
| // • the scanning filter policy uses the Filter Accept List and scanning |
| // is enabled, or |
| // • the initiator filter policy uses the Filter Accept List and an |
| // HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection |
| // command is pending. |
| if (FilterAcceptListBusy()) { |
| LOG_INFO( |
| "device is currently advertising, scanning," |
| " or establishing an LE connection using the filter accept list"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| for (auto it = le_filter_accept_list_.begin(); |
| it != le_filter_accept_list_.end(); it++) { |
| // Address shall be ignored when Address_Type is set to 0xFF. |
| if (it->address_type == address_type && |
| (address_type == FilterAcceptListAddressType::ANONYMOUS_ADVERTISERS || |
| it->address == address)) { |
| le_filter_accept_list_.erase(it); |
| return ErrorCode::SUCCESS; |
| } |
| } |
| |
| // Note: this case is not documented. |
| LOG_INFO("address not found in the filter accept list"); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // LE Periodic Advertiser List |
| // ============================================================================= |
| |
| // HCI LE Add Device To Periodic Advertiser List command (Vol 4, Part E |
| // § 7.8.70). |
| ErrorCode LinkLayerController::LeAddDeviceToPeriodicAdvertiserList( |
| bluetooth::hci::AdvertiserAddressType advertiser_address_type, |
| Address advertiser_address, uint8_t advertising_sid) { |
| // If the Host issues this command when an HCI_LE_Periodic_Advertising_- |
| // Create_Sync command is pending, the Controller shall return the error code |
| // Command Disallowed (0x0C). |
| if (synchronizing_.has_value()) { |
| LOG_INFO( |
| "LE Periodic Advertising Create Sync command is currently pending"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // When a Controller cannot add an entry to the Periodic Advertiser list |
| // because the list is full, the Controller shall return the error code Memory |
| // Capacity Exceeded (0x07). |
| if (le_periodic_advertiser_list_.size() >= |
| properties_.le_periodic_advertiser_list_size) { |
| LOG_INFO("periodic advertiser list is full"); |
| return ErrorCode::MEMORY_CAPACITY_EXCEEDED; |
| } |
| |
| // If the entry is already on the list, the Controller shall |
| // return the error code Invalid HCI Command Parameters (0x12). |
| for (auto& entry : le_periodic_advertiser_list_) { |
| if (entry.advertiser_address_type == advertiser_address_type && |
| entry.advertiser_address == advertiser_address && |
| entry.advertising_sid == advertising_sid) { |
| LOG_INFO("entry is already found in the periodic advertiser list"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| } |
| |
| le_periodic_advertiser_list_.emplace_back(PeriodicAdvertiserListEntry{ |
| advertiser_address_type, advertiser_address, advertising_sid}); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Remove Device From Periodic Advertiser List command |
| // (Vol 4, Part E § 7.8.71). |
| ErrorCode LinkLayerController::LeRemoveDeviceFromPeriodicAdvertiserList( |
| bluetooth::hci::AdvertiserAddressType advertiser_address_type, |
| Address advertiser_address, uint8_t advertising_sid) { |
| // If this command is used when an HCI_LE_Periodic_Advertising_Create_Sync |
| // command is pending, the Controller shall return the error code Command |
| // Disallowed (0x0C). |
| if (synchronizing_.has_value()) { |
| LOG_INFO( |
| "LE Periodic Advertising Create Sync command is currently pending"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| for (auto it = le_periodic_advertiser_list_.begin(); |
| it != le_periodic_advertiser_list_.end(); it++) { |
| if (it->advertiser_address_type == advertiser_address_type && |
| it->advertiser_address == advertiser_address && |
| it->advertising_sid == advertising_sid) { |
| le_periodic_advertiser_list_.erase(it); |
| return ErrorCode::SUCCESS; |
| } |
| } |
| |
| // When a Controller cannot remove an entry from the Periodic Advertiser list |
| // because it is not found, the Controller shall return the error code Unknown |
| // Advertising Identifier (0x42). |
| LOG_INFO("entry not found in the periodic advertiser list"); |
| return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER; |
| } |
| |
| // HCI LE Clear Periodic Advertiser List command (Vol 4, Part E § 7.8.72). |
| ErrorCode LinkLayerController::LeClearPeriodicAdvertiserList() { |
| // If this command is used when an HCI_LE_Periodic_Advertising_Create_Sync |
| // command is pending, the Controller shall return the error code Command |
| // Disallowed (0x0C). |
| if (synchronizing_.has_value()) { |
| LOG_INFO( |
| "LE Periodic Advertising Create Sync command is currently pending"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| le_periodic_advertiser_list_.clear(); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // LE Periodic Sync |
| // ============================================================================= |
| |
| // HCI LE Periodic Advertising Create Sync command (Vol 4, Part E § 7.8.67). |
| ErrorCode LinkLayerController::LePeriodicAdvertisingCreateSync( |
| bluetooth::hci::PeriodicAdvertisingOptions options, uint8_t advertising_sid, |
| bluetooth::hci::AdvertiserAddressType advertiser_address_type, |
| Address advertiser_address, uint16_t /*skip*/, uint16_t sync_timeout, |
| uint8_t sync_cte_type) { |
| // If the Host issues this command when another HCI_LE_Periodic_Advertising_- |
| // Create_Sync command is pending, the Controller shall return the error code |
| // Command Disallowed (0x0C). |
| if (synchronizing_.has_value()) { |
| LOG_INFO( |
| "LE Periodic Advertising Create Sync command is currently pending"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the Host sets all the non-reserved bits of the Sync_CTE_Type parameter |
| // to 1, the Controller shall return the error code Command Disallowed (0x0C). |
| uint8_t sync_cte_type_mask = 0x1f; |
| if ((sync_cte_type & sync_cte_type_mask) == sync_cte_type_mask) { |
| LOG_INFO( |
| "Sync_CTE_Type is configured to ignore all types of advertisement"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the Host issues this command with bit 0 of Options not set and with |
| // Advertising_SID, Advertiser_Address_Type, and Advertiser_Address the same |
| // as those of a periodic advertising train that the Controller is already |
| // synchronized to, the Controller shall return the error code |
| // Connection Already Exists (0x0B). |
| bool has_synchronized_train = false; |
| for (auto& [_, sync] : synchronized_) { |
| has_synchronized_train |= |
| sync.advertiser_address_type == advertiser_address_type && |
| sync.advertiser_address == advertiser_address && |
| sync.advertising_sid == advertising_sid; |
| } |
| if (!options.use_periodic_advertiser_list_ && has_synchronized_train) { |
| LOG_INFO( |
| "the controller is already synchronized on the periodic advertising" |
| " train from %s[%s] - SID=0x%x", |
| advertiser_address.ToString().c_str(), |
| bluetooth::hci::AdvertiserAddressTypeText(advertiser_address_type) |
| .c_str(), |
| advertising_sid); |
| return ErrorCode::CONNECTION_ALREADY_EXISTS; |
| } |
| |
| // If the Host issues this command and the Controller has insufficient |
| // resources to handle any more periodic advertising trains, the Controller |
| // shall return the error code Memory Capacity Exceeded (0x07) |
| // TODO emulate LE state limits. |
| |
| // If bit 1 of Options is set to 0, bit 2 is set to 1, and the Controller does |
| // not support the Periodic Advertising ADI Support feature, then the |
| // Controller shall return an error which should use the error code |
| // Unsupported Feature or Parameter Value (0x11). |
| if (!options.disable_reporting_ && options.enable_duplicate_filtering_ && |
| !properties_.SupportsLLFeature( |
| LLFeaturesBits::PERIODIC_ADVERTISING_ADI_SUPPORT)) { |
| LOG_INFO( |
| "reporting and duplicate filtering are enabled in the options," |
| " but the controller does not support the Periodic Advertising ADI" |
| " Support feature"); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // If bit 1 of the Options parameter is set to 1 and the Controller does not |
| // support the HCI_LE_Set_Periodic_Advertising_Receive_Enable command, the |
| // Controller shall return the error code Connection Failed to be Established |
| // / Synchronization Timeout (0x3E). |
| if (options.disable_reporting_ && |
| !properties_.SupportsCommand( |
| bluetooth::hci::OpCodeIndex:: |
| LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE)) { |
| LOG_INFO( |
| "reporting is disabled in the options, but the controller does not" |
| " support the HCI_LE_Set_Periodic_Advertising_Receive_Enable command"); |
| return ErrorCode::CONNECTION_FAILED_ESTABLISHMENT; |
| } |
| |
| synchronizing_ = Synchronizing{ |
| .options = options, |
| .advertiser_address_type = advertiser_address_type, |
| .advertiser_address = advertiser_address, |
| .advertising_sid = advertising_sid, |
| .sync_timeout = 10ms * sync_timeout, |
| }; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Periodic Advertising Create Sync Cancel command (Vol 4, Part E |
| // § 7.8.68). |
| ErrorCode LinkLayerController::LePeriodicAdvertisingCreateSyncCancel() { |
| // If the Host issues this command while no HCI_LE_Periodic_Advertising_- |
| // Create_Sync command is pending, the Controller shall return the error code |
| // Command Disallowed (0x0C). |
| if (!synchronizing_.has_value()) { |
| LOG_INFO("no LE Periodic Advertising Create Sync command is pending"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // After the HCI_Command_Complete is sent and if the cancellation was |
| // successful, the Controller sends an HCI_LE_Periodic_Advertising_Sync_- |
| // Established event to the Host with the error code Operation Cancelled |
| // by Host (0x44). |
| if (IsLeEventUnmasked(SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED)) { |
| ScheduleTask(0ms, [this] { |
| send_event_( |
| bluetooth::hci::LePeriodicAdvertisingSyncEstablishedBuilder::Create( |
| ErrorCode::OPERATION_CANCELLED_BY_HOST, 0, 0, |
| AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, |
| bluetooth::hci::SecondaryPhyType::NO_PACKETS, 0, |
| bluetooth::hci::ClockAccuracy::PPM_500)); |
| }); |
| } |
| |
| synchronizing_ = {}; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Periodic Advertising Terminate Sync command (Vol 4, Part E |
| // § 7.8.69). |
| ErrorCode LinkLayerController::LePeriodicAdvertisingTerminateSync( |
| uint16_t sync_handle) { |
| // If the periodic advertising train corresponding to the Sync_Handle |
| // parameter does not exist, then the Controller shall return the error |
| // code Unknown Advertising Identifier (0x42). |
| if (synchronized_.count(sync_handle) == 0) { |
| LOG_INFO("the Sync_Handle 0x%x does not exist", sync_handle); |
| return ErrorCode::UNKNOWN_ADVERTISING_IDENTIFIER; |
| } |
| |
| synchronized_.erase(sync_handle); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // LE Legacy Scanning |
| // ============================================================================= |
| |
| // HCI command LE_Set_Scan_Parameters (Vol 4, Part E § 7.8.10). |
| ErrorCode LinkLayerController::LeSetScanParameters( |
| bluetooth::hci::LeScanType scan_type, uint16_t scan_interval, |
| uint16_t scan_window, bluetooth::hci::OwnAddressType own_address_type, |
| bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy) { |
| // Legacy advertising commands are disallowed when extended advertising |
| // commands were used since the last reset. |
| if (!SelectLegacyAdvertising()) { |
| LOG_INFO( |
| "legacy advertising command rejected because extended advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // The Host shall not issue this command when scanning is enabled in the |
| // Controller; if it is the Command Disallowed error code shall be used. |
| if (scanner_.IsEnabled()) { |
| LOG_INFO("scanning is currently enabled"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // Note: no explicit error code stated for invalid interval and window |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (scan_interval < 0x4 || scan_interval > 0x4000 || scan_window < 0x4 || |
| scan_window > 0x4000) { |
| LOG_INFO( |
| "le_scan_interval (0x%04x) and/or" |
| " le_scan_window (0x%04x) are outside the range" |
| " of supported values (0x0004 - 0x4000)", |
| scan_interval, scan_window); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The LE_Scan_Window parameter shall always be set to a value smaller |
| // or equal to the value set for the LE_Scan_Interval parameter. |
| if (scan_window > scan_interval) { |
| LOG_INFO("le_scan_window (0x%04x) is larger than le_scan_interval (0x%04x)", |
| scan_window, scan_interval); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| scanner_.le_1m_phy.enabled = true; |
| scanner_.le_coded_phy.enabled = false; |
| scanner_.le_1m_phy.scan_type = scan_type; |
| scanner_.le_1m_phy.scan_interval = scan_interval; |
| scanner_.le_1m_phy.scan_window = scan_window; |
| scanner_.own_address_type = own_address_type; |
| scanner_.scan_filter_policy = scanning_filter_policy; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI command LE_Set_Scan_Enable (Vol 4, Part E § 7.8.11). |
| ErrorCode LinkLayerController::LeSetScanEnable(bool enable, |
| bool filter_duplicates) { |
| // Legacy advertising commands are disallowed when extended advertising |
| // commands were used since the last reset. |
| if (!SelectLegacyAdvertising()) { |
| LOG_INFO( |
| "legacy advertising command rejected because extended advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| if (!enable) { |
| scanner_.scan_enable = false; |
| scanner_.history.clear(); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // TODO: additional checks would apply in the case of a LE only Controller |
| // with no configured public device address. |
| |
| // If LE_Scan_Enable is set to 0x01, the scanning parameters' Own_Address_Type |
| // parameter is set to 0x01 or 0x03, 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 ((scanner_.own_address_type == |
| bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS || |
| scanner_.own_address_type == |
| bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) && |
| random_address_ == Address::kEmpty) { |
| LOG_INFO( |
| "own_address_type is Random_Device_Address or" |
| " Resolvable_or_Random_Address but the Random_Address" |
| " has not been initialized"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| scanner_.scan_enable = true; |
| scanner_.history.clear(); |
| scanner_.timeout = {}; |
| scanner_.periodical_timeout = {}; |
| scanner_.filter_duplicates = filter_duplicates |
| ? bluetooth::hci::FilterDuplicates::ENABLED |
| : bluetooth::hci::FilterDuplicates::DISABLED; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // LE Extended Scanning |
| // ============================================================================= |
| |
| // HCI command LE_Set_Extended_Scan_Parameters (Vol 4, Part E § 7.8.64). |
| ErrorCode LinkLayerController::LeSetExtendedScanParameters( |
| bluetooth::hci::OwnAddressType own_address_type, |
| bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy, |
| uint8_t scanning_phys, |
| std::vector<bluetooth::hci::PhyScanParameters> scanning_phy_parameters) { |
| uint8_t supported_phys = properties_.LeSupportedPhys(); |
| |
| // Extended advertising commands are disallowed when legacy advertising |
| // commands were used since the last reset. |
| if (!SelectExtendedAdvertising()) { |
| LOG_INFO( |
| "extended advertising command rejected because legacy advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the Host issues this command when scanning is enabled in the Controller, |
| // the Controller shall return the error code Command Disallowed (0x0C). |
| if (scanner_.IsEnabled()) { |
| LOG_INFO("scanning is currently enabled"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the Host specifies a PHY that is not supported by the Controller, |
| // including a bit that is reserved for future use, it should return the |
| // error code Unsupported Feature or Parameter Value (0x11). |
| if ((scanning_phys & ~supported_phys) != 0) { |
| LOG_INFO( |
| "scanning_phys (%02x) enables PHYs that are not supported by" |
| " the controller", |
| scanning_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // TODO(c++20) std::popcount |
| if (__builtin_popcount(scanning_phys) != |
| int(scanning_phy_parameters.size())) { |
| LOG_INFO( |
| "scanning_phy_parameters (%zu)" |
| " does not match scanning_phys (%02x)", |
| scanning_phy_parameters.size(), scanning_phys); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // Note: no explicit error code stated for empty scanning_phys |
| // but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on HCI Extended LE Create Connecton command. |
| if (scanning_phys == 0) { |
| LOG_INFO("scanning_phys is empty"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| for (auto const& parameter : scanning_phy_parameters) { |
| // If the requested scan cannot be supported by the implementation, |
| // the Controller shall return the error code |
| // Invalid HCI Command Parameters (0x12). |
| if (parameter.le_scan_interval_ < 0x4 || parameter.le_scan_window_ < 0x4) { |
| LOG_INFO( |
| "le_scan_interval (0x%04x) and/or" |
| " le_scan_window (0x%04x) are outside the range" |
| " of supported values (0x0004 - 0xffff)", |
| parameter.le_scan_interval_, parameter.le_scan_window_); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| if (parameter.le_scan_window_ > parameter.le_scan_interval_) { |
| LOG_INFO( |
| "le_scan_window (0x%04x) is larger than le_scan_interval (0x%04x)", |
| parameter.le_scan_window_, parameter.le_scan_interval_); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| } |
| |
| scanner_.own_address_type = own_address_type; |
| scanner_.scan_filter_policy = scanning_filter_policy; |
| scanner_.le_1m_phy.enabled = false; |
| scanner_.le_coded_phy.enabled = false; |
| int offset = 0; |
| |
| if (scanning_phys & 0x1) { |
| scanner_.le_1m_phy = Scanner::PhyParameters{ |
| .enabled = true, |
| .scan_type = scanning_phy_parameters[offset].le_scan_type_, |
| .scan_interval = scanning_phy_parameters[offset].le_scan_interval_, |
| .scan_window = scanning_phy_parameters[offset].le_scan_window_, |
| }; |
| offset++; |
| } |
| |
| if (scanning_phys & 0x4) { |
| scanner_.le_coded_phy = Scanner::PhyParameters{ |
| .enabled = true, |
| .scan_type = scanning_phy_parameters[offset].le_scan_type_, |
| .scan_interval = scanning_phy_parameters[offset].le_scan_interval_, |
| .scan_window = scanning_phy_parameters[offset].le_scan_window_, |
| }; |
| offset++; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI command LE_Set_Extended_Scan_Enable (Vol 4, Part E § 7.8.65). |
| ErrorCode LinkLayerController::LeSetExtendedScanEnable( |
| bool enable, bluetooth::hci::FilterDuplicates filter_duplicates, |
| uint16_t duration, uint16_t period) { |
| // Extended advertising commands are disallowed when legacy advertising |
| // commands were used since the last reset. |
| if (!SelectExtendedAdvertising()) { |
| LOG_INFO( |
| "extended advertising command rejected because legacy advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| if (!enable) { |
| scanner_.scan_enable = false; |
| scanner_.history.clear(); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // The Period parameter shall be ignored when the Duration parameter is zero. |
| if (duration == 0) { |
| period = 0; |
| } |
| |
| // If Filter_Duplicates is set to 0x02 and either Period or Duration to zero, |
| // the Controller shall return the error code |
| // Invalid HCI Command Parameters (0x12). |
| if (filter_duplicates == |
| bluetooth::hci::FilterDuplicates::RESET_EACH_PERIOD && |
| (period == 0 || duration == 0)) { |
| LOG_INFO( |
| "filter_duplicates is Reset_Each_Period but either" |
| " the period or duration is 0"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| auto duration_ms = std::chrono::milliseconds(10 * duration); |
| auto period_ms = std::chrono::milliseconds(1280 * period); |
| |
| // If both the Duration and Period parameters are non-zero and the Duration is |
| // greater than or equal to the Period, the Controller shall return the |
| // error code Invalid HCI Command Parameters (0x12). |
| if (period != 0 && duration != 0 && duration_ms >= period_ms) { |
| LOG_INFO("the period is greater than or equal to the duration"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // TODO: additional checks would apply in the case of a LE only Controller |
| // with no configured public device address. |
| |
| // If LE_Scan_Enable is set to 0x01, the scanning parameters' Own_Address_Type |
| // parameter is set to 0x01 or 0x03, 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 ((scanner_.own_address_type == |
| bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS || |
| scanner_.own_address_type == |
| bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) && |
| random_address_ == Address::kEmpty) { |
| LOG_INFO( |
| "own_address_type is Random_Device_Address or" |
| " Resolvable_or_Random_Address but the Random_Address" |
| " has not been initialized"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| scanner_.scan_enable = true; |
| scanner_.history.clear(); |
| scanner_.timeout = {}; |
| scanner_.periodical_timeout = {}; |
| scanner_.filter_duplicates = filter_duplicates; |
| scanner_.duration = duration_ms; |
| scanner_.period = period_ms; |
| |
| auto now = std::chrono::steady_clock::now(); |
| |
| // At the end of a single scan (Duration non-zero but Period zero), an |
| // HCI_LE_Scan_Timeout event shall be generated. |
| if (duration != 0) { |
| scanner_.timeout = now + scanner_.duration; |
| } |
| if (period != 0) { |
| scanner_.periodical_timeout = now + scanner_.period; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // LE Legacy Connection |
| // ============================================================================= |
| |
| // HCI LE Create Connection command (Vol 4, Part E § 7.8.12). |
| ErrorCode LinkLayerController::LeCreateConnection( |
| uint16_t scan_interval, uint16_t scan_window, |
| bluetooth::hci::InitiatorFilterPolicy initiator_filter_policy, |
| AddressWithType peer_address, |
| bluetooth::hci::OwnAddressType own_address_type, |
| uint16_t connection_interval_min, uint16_t connection_interval_max, |
| uint16_t max_latency, uint16_t supervision_timeout, uint16_t min_ce_length, |
| uint16_t max_ce_length) { |
| // Legacy advertising commands are disallowed when extended advertising |
| // commands were used since the last reset. |
| if (!SelectLegacyAdvertising()) { |
| LOG_INFO( |
| "legacy advertising command rejected because extended advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the Host issues this command when another HCI_LE_Create_Connection |
| // command is pending in the Controller, the Controller shall return the |
| // error code Command Disallowed (0x0C). |
| if (initiator_.IsEnabled()) { |
| LOG_INFO("initiator is currently enabled"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // Note: no explicit error code stated for invalid interval and window |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (scan_interval < 0x4 || scan_interval > 0x4000 || scan_window < 0x4 || |
| scan_window > 0x4000) { |
| LOG_INFO( |
| "scan_interval (0x%04x) and/or " |
| "scan_window (0x%04x) are outside the range" |
| " of supported values (0x4 - 0x4000)", |
| scan_interval, scan_window); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The LE_Scan_Window parameter shall be set to a value smaller or equal to |
| // the value set for the LE_Scan_Interval parameter. |
| if (scan_interval < scan_window) { |
| LOG_INFO("scan_window (0x%04x) is larger than scan_interval (0x%04x)", |
| scan_window, scan_interval); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // Note: no explicit error code stated for invalid connection interval |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (connection_interval_min < 0x6 || connection_interval_min > 0x0c80 || |
| connection_interval_max < 0x6 || connection_interval_max > 0x0c80) { |
| LOG_INFO( |
| "connection_interval_min (0x%04x) and/or " |
| "connection_interval_max (0x%04x) are outside the range" |
| " of supported values (0x6 - 0x0c80)", |
| connection_interval_min, connection_interval_max); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The Connection_Interval_Min parameter shall not be greater than the |
| // Connection_Interval_Max parameter. |
| if (connection_interval_max < connection_interval_min) { |
| LOG_INFO( |
| "connection_interval_min (0x%04x) is larger than" |
| " connection_interval_max (0x%04x)", |
| connection_interval_min, connection_interval_max); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // Note: no explicit error code stated for invalid max_latency |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (max_latency > 0x01f3) { |
| LOG_INFO( |
| "max_latency (0x%04x) is outside the range" |
| " of supported values (0x0 - 0x01f3)", |
| max_latency); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // Note: no explicit error code stated for invalid supervision timeout |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (supervision_timeout < 0xa || supervision_timeout > 0x0c80) { |
| LOG_INFO( |
| "supervision_timeout (0x%04x) is outside the range" |
| " of supported values (0xa - 0x0c80)", |
| supervision_timeout); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The Supervision_Timeout in milliseconds shall be larger than |
| // (1 + Max_Latency) * Connection_Interval_Max * 2, where |
| // Connection_Interval_Max is given in milliseconds. |
| milliseconds min_supervision_timeout = duration_cast<milliseconds>( |
| (1 + max_latency) * slots(2 * connection_interval_max) * 2); |
| if (supervision_timeout * 10ms < min_supervision_timeout) { |
| LOG_INFO( |
| "supervision_timeout (%d ms) is smaller that the minimal supervision " |
| "timeout allowed by connection_interval_max and max_latency (%u ms)", |
| supervision_timeout * 10, |
| static_cast<unsigned>(min_supervision_timeout / 1ms)); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // TODO: additional checks would apply in the case of a LE only Controller |
| // with no configured public device address. |
| |
| // If the 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 (own_address_type == OwnAddressType::RANDOM_DEVICE_ADDRESS && |
| random_address_ == Address::kEmpty) { |
| LOG_INFO( |
| "own_address_type is Random_Device_Address but the Random_Address" |
| " has not been initialized"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If the Own_Address_Type parameter is set to 0x03, the |
| // Initiator_Filter_Policy parameter is set to 0x00, the controller's |
| // resolving list did not contain 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 (own_address_type == OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS && |
| initiator_filter_policy == InitiatorFilterPolicy::USE_PEER_ADDRESS && |
| !GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local) && |
| random_address_ == Address::kEmpty) { |
| LOG_INFO( |
| "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; |
| } |
| |
| initiator_.connect_enable = true; |
| initiator_.initiator_filter_policy = initiator_filter_policy; |
| initiator_.peer_address = peer_address; |
| initiator_.own_address_type = own_address_type; |
| initiator_.le_1m_phy.enabled = true; |
| initiator_.le_1m_phy.scan_interval = scan_interval; |
| initiator_.le_1m_phy.scan_window = scan_window; |
| initiator_.le_1m_phy.connection_interval_min = connection_interval_min; |
| initiator_.le_1m_phy.connection_interval_max = connection_interval_max; |
| initiator_.le_1m_phy.max_latency = max_latency; |
| initiator_.le_1m_phy.supervision_timeout = supervision_timeout; |
| initiator_.le_1m_phy.min_ce_length = min_ce_length; |
| initiator_.le_1m_phy.max_ce_length = max_ce_length; |
| initiator_.le_2m_phy.enabled = false; |
| initiator_.le_coded_phy.enabled = false; |
| initiator_.pending_connect_request = {}; |
| return ErrorCode::SUCCESS; |
| } |
| |
| // HCI LE Create Connection Cancel command (Vol 4, Part E § 7.8.12). |
| ErrorCode LinkLayerController::LeCreateConnectionCancel() { |
| // If no HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection |
| // command is pending, then the Controller shall return the error code |
| // Command Disallowed (0x0C). |
| if (!initiator_.IsEnabled()) { |
| LOG_INFO("initiator is currently disabled"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the cancellation was successful then, after the HCI_Command_Complete |
| // event for the HCI_LE_Create_Connection_Cancel command, either an LE |
| // Connection Complete or an HCI_LE_Enhanced_Connection_Complete event |
| // shall be generated. In either case, the event shall be sent with the error |
| // code Unknown Connection Identifier (0x02). |
| if (IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) { |
| ScheduleTask(0ms, [this] { |
| send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create( |
| ErrorCode::UNKNOWN_CONNECTION, 0, Role::CENTRAL, |
| AddressType::PUBLIC_DEVICE_ADDRESS, Address(), Address(), Address(), |
| 0, 0, 0, bluetooth::hci::ClockAccuracy::PPM_500)); |
| }); |
| } else if (IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) { |
| ScheduleTask(0ms, [this] { |
| send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create( |
| ErrorCode::UNKNOWN_CONNECTION, 0, Role::CENTRAL, |
| AddressType::PUBLIC_DEVICE_ADDRESS, Address(), 0, 0, 0, |
| bluetooth::hci::ClockAccuracy::PPM_500)); |
| }); |
| } |
| |
| initiator_.Disable(); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // ============================================================================= |
| // LE Extended Connection |
| // ============================================================================= |
| |
| // HCI LE Extended Create Connection command (Vol 4, Part E § 7.8.66). |
| ErrorCode LinkLayerController::LeExtendedCreateConnection( |
| bluetooth::hci::InitiatorFilterPolicy initiator_filter_policy, |
| bluetooth::hci::OwnAddressType own_address_type, |
| AddressWithType peer_address, uint8_t initiating_phys, |
| std::vector<bluetooth::hci::LeCreateConnPhyScanParameters> |
| initiating_phy_parameters) { |
| // Extended advertising commands are disallowed when legacy advertising |
| // commands were used since the last reset. |
| if (!SelectExtendedAdvertising()) { |
| LOG_INFO( |
| "extended advertising command rejected because legacy advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the Host issues this command when another |
| // HCI_LE_Extended_Create_Connection command is pending in the Controller, |
| // the Controller shall return the error code Command Disallowed (0x0C). |
| if (initiator_.IsEnabled()) { |
| LOG_INFO("initiator is currently enabled"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the Host specifies a PHY that is not supported by the Controller, |
| // including a bit that is reserved for future use, the latter should return |
| // the error code Unsupported Feature or Parameter Value (0x11). |
| if ((initiating_phys & 0xf8) != 0) { |
| LOG_INFO( |
| "initiating_phys (%02x) enables PHYs that are not supported by" |
| " the controller", |
| initiating_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // TODO(c++20) std::popcount |
| if (__builtin_popcount(initiating_phys) != |
| int(initiating_phy_parameters.size())) { |
| LOG_INFO( |
| "initiating_phy_parameters (%zu)" |
| " does not match initiating_phys (%02x)", |
| initiating_phy_parameters.size(), initiating_phys); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If the Initiating_PHYs parameter does not have at least one bit set for a |
| // PHY allowed for scanning on the primary advertising physical channel, the |
| // Controller shall return the error code |
| // Invalid HCI Command Parameters (0x12). |
| if (initiating_phys == 0) { |
| LOG_INFO("initiating_phys is empty"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| for (auto const& parameter : initiating_phy_parameters) { |
| // Note: no explicit error code stated for invalid interval and window |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (parameter.scan_interval_ < 0x4 || parameter.scan_interval_ > 0x4000 || |
| parameter.scan_window_ < 0x4 || parameter.scan_window_ > 0x4000) { |
| LOG_INFO( |
| "scan_interval (0x%04x) and/or " |
| "scan_window (0x%04x) are outside the range" |
| " of supported values (0x4 - 0x4000)", |
| parameter.scan_interval_, parameter.scan_window_); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The LE_Scan_Window parameter shall be set to a value smaller or equal to |
| // the value set for the LE_Scan_Interval parameter. |
| if (parameter.scan_interval_ < parameter.scan_window_) { |
| LOG_INFO("scan_window (0x%04x) is larger than scan_interval (0x%04x)", |
| parameter.scan_window_, parameter.scan_interval_); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // Note: no explicit error code stated for invalid connection interval |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (parameter.conn_interval_min_ < 0x6 || |
| parameter.conn_interval_min_ > 0x0c80 || |
| parameter.conn_interval_max_ < 0x6 || |
| parameter.conn_interval_max_ > 0x0c80) { |
| LOG_INFO( |
| "connection_interval_min (0x%04x) and/or " |
| "connection_interval_max (0x%04x) are outside the range" |
| " of supported values (0x6 - 0x0c80)", |
| parameter.conn_interval_min_, parameter.conn_interval_max_); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The Connection_Interval_Min parameter shall not be greater than the |
| // Connection_Interval_Max parameter. |
| if (parameter.conn_interval_max_ < parameter.conn_interval_min_) { |
| LOG_INFO( |
| "connection_interval_min (0x%04x) is larger than" |
| " connection_interval_max (0x%04x)", |
| parameter.conn_interval_min_, parameter.conn_interval_max_); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // Note: no explicit error code stated for invalid max_latency |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (parameter.conn_latency_ > 0x01f3) { |
| LOG_INFO( |
| "max_latency (0x%04x) is outside the range" |
| " of supported values (0x0 - 0x01f3)", |
| parameter.conn_latency_); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // Note: no explicit error code stated for invalid supervision timeout |
| // values but assuming Unsupported Feature or Parameter Value (0x11) |
| // error code based on similar advertising command. |
| if (parameter.supervision_timeout_ < 0xa || |
| parameter.supervision_timeout_ > 0x0c80) { |
| LOG_INFO( |
| "supervision_timeout (0x%04x) is outside the range" |
| " of supported values (0xa - 0x0c80)", |
| parameter.supervision_timeout_); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| |
| // The Supervision_Timeout in milliseconds shall be larger than |
| // (1 + Max_Latency) * Connection_Interval_Max * 2, where |
| // Connection_Interval_Max is given in milliseconds. |
| milliseconds min_supervision_timeout = duration_cast<milliseconds>( |
| (1 + parameter.conn_latency_) * |
| slots(2 * parameter.conn_interval_max_) * 2); |
| if (parameter.supervision_timeout_ * 10ms < min_supervision_timeout) { |
| LOG_INFO( |
| "supervision_timeout (%d ms) is smaller that the minimal supervision " |
| "timeout allowed by connection_interval_max and max_latency (%u ms)", |
| parameter.supervision_timeout_ * 10, |
| static_cast<unsigned>(min_supervision_timeout / 1ms)); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| } |
| |
| // TODO: additional checks would apply in the case of a LE only Controller |
| // with no configured public device address. |
| |
| // If the 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 (own_address_type == OwnAddressType::RANDOM_DEVICE_ADDRESS && |
| random_address_ == Address::kEmpty) { |
| LOG_INFO( |
| "own_address_type is Random_Device_Address but the Random_Address" |
| " has not been initialized"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // If the Own_Address_Type parameter is set to 0x03, the |
| // Initiator_Filter_Policy parameter is set to 0x00, the controller's |
| // resolving list did not contain 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 (own_address_type == OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS && |
| initiator_filter_policy == InitiatorFilterPolicy::USE_PEER_ADDRESS && |
| !GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local) && |
| random_address_ == Address::kEmpty) { |
| LOG_INFO( |
| "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; |
| } |
| |
| initiator_.connect_enable = true; |
| initiator_.initiator_filter_policy = initiator_filter_policy; |
| initiator_.peer_address = peer_address; |
| initiator_.own_address_type = own_address_type; |
| initiator_.pending_connect_request = {}; |
| |
| initiator_.le_1m_phy.enabled = false; |
| initiator_.le_2m_phy.enabled = false; |
| initiator_.le_coded_phy.enabled = false; |
| int offset = 0; |
| |
| if (initiating_phys & 0x1) { |
| initiator_.le_1m_phy = Initiator::PhyParameters{ |
| .enabled = true, |
| .scan_interval = initiating_phy_parameters[offset].scan_interval_, |
| .scan_window = initiating_phy_parameters[offset].scan_window_, |
| .connection_interval_min = |
| initiating_phy_parameters[offset].conn_interval_min_, |
| .connection_interval_max = |
| initiating_phy_parameters[offset].conn_interval_max_, |
| .max_latency = initiating_phy_parameters[offset].conn_latency_, |
| .supervision_timeout = |
| initiating_phy_parameters[offset].supervision_timeout_, |
| .min_ce_length = initiating_phy_parameters[offset].min_ce_length_, |
| .max_ce_length = initiating_phy_parameters[offset].max_ce_length_, |
| }; |
| offset++; |
| } |
| |
| if (initiating_phys & 0x2) { |
| initiator_.le_2m_phy = Initiator::PhyParameters{ |
| .enabled = true, |
| .scan_interval = initiating_phy_parameters[offset].scan_interval_, |
| .scan_window = initiating_phy_parameters[offset].scan_window_, |
| .connection_interval_min = |
| initiating_phy_parameters[offset].conn_interval_min_, |
| .connection_interval_max = |
| initiating_phy_parameters[offset].conn_interval_max_, |
| .max_latency = initiating_phy_parameters[offset].conn_latency_, |
| .supervision_timeout = |
| initiating_phy_parameters[offset].supervision_timeout_, |
| .min_ce_length = initiating_phy_parameters[offset].min_ce_length_, |
| .max_ce_length = initiating_phy_parameters[offset].max_ce_length_, |
| }; |
| offset++; |
| } |
| |
| if (initiating_phys & 0x4) { |
| initiator_.le_coded_phy = Initiator::PhyParameters{ |
| .enabled = true, |
| .scan_interval = initiating_phy_parameters[offset].scan_interval_, |
| .scan_window = initiating_phy_parameters[offset].scan_window_, |
| .connection_interval_min = |
| initiating_phy_parameters[offset].conn_interval_min_, |
| .connection_interval_max = |
| initiating_phy_parameters[offset].conn_interval_max_, |
| .max_latency = initiating_phy_parameters[offset].conn_latency_, |
| .supervision_timeout = |
| initiating_phy_parameters[offset].supervision_timeout_, |
| .min_ce_length = initiating_phy_parameters[offset].min_ce_length_, |
| .max_ce_length = initiating_phy_parameters[offset].max_ce_length_, |
| }; |
| offset++; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::SetSecureSimplePairingSupport(bool enable) { |
| uint64_t bit = 0x1; |
| secure_simple_pairing_host_support_ = enable; |
| if (enable) { |
| host_supported_features_ |= bit; |
| } else { |
| host_supported_features_ &= ~bit; |
| } |
| } |
| |
| void LinkLayerController::SetLeHostSupport(bool enable) { |
| // TODO: Vol 2, Part C § 3.5 Feature requirements. |
| // (65) LE Supported (Host) implies |
| // (38) LE Supported (Controller) |
| uint64_t bit = 0x2; |
| le_host_support_ = enable; |
| if (enable) { |
| host_supported_features_ |= bit; |
| } else { |
| host_supported_features_ &= ~bit; |
| } |
| } |
| |
| void LinkLayerController::SetSecureConnectionsSupport(bool enable) { |
| // TODO: Vol 2, Part C § 3.5 Feature requirements. |
| // (67) Secure Connections (Host Support) implies |
| // (64) Secure Simple Pairing (Host Support) and |
| // (136) Secure Connections (Controller Support) |
| uint64_t bit = 0x8; |
| secure_connections_host_support_ = enable; |
| if (enable) { |
| host_supported_features_ |= bit; |
| } else { |
| host_supported_features_ &= ~bit; |
| } |
| } |
| |
| void LinkLayerController::SetLocalName( |
| std::array<uint8_t, kLocalNameSize> const& local_name) { |
| std::copy(local_name.begin(), local_name.end(), local_name_.begin()); |
| } |
| |
| void LinkLayerController::SetLocalName(std::vector<uint8_t> const& local_name) { |
| ASSERT(local_name.size() <= local_name_.size()); |
| local_name_.fill(0); |
| std::copy(local_name.begin(), local_name.end(), local_name_.begin()); |
| } |
| |
| void LinkLayerController::SetExtendedInquiryResponse( |
| std::vector<uint8_t> const& extended_inquiry_response) { |
| ASSERT(extended_inquiry_response.size() <= extended_inquiry_response_.size()); |
| extended_inquiry_response_.fill(0); |
| std::copy(extended_inquiry_response.begin(), extended_inquiry_response.end(), |
| extended_inquiry_response_.begin()); |
| } |
| |
| LinkLayerController::LinkLayerController(const Address& address, |
| const ControllerProperties& properties) |
| : address_(address), |
| properties_(properties), |
| lm_(nullptr, link_manager_destroy) { |
| |
| if (properties_.quirks.has_default_random_address) { |
| LOG_WARN("Configuring a default random address for this controller"); |
| random_address_ = Address { 0xba, 0xdb, 0xad, 0xba, 0xdb, 0xad }; |
| } |
| |
| ops_ = { |
| .user_pointer = this, |
| .get_handle = |
| [](void* user, const uint8_t(*address)[6]) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| |
| return controller->connections_.GetHandleOnlyAddress( |
| Address(*address)); |
| }, |
| |
| .get_address = |
| [](void* user, uint16_t handle, uint8_t(*result)[6]) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| |
| auto address_opt = controller->connections_.GetAddressSafe(handle); |
| Address address = address_opt.has_value() |
| ? address_opt.value().GetAddress() |
| : Address::kEmpty; |
| std::copy(address.data(), address.data() + 6, |
| reinterpret_cast<uint8_t*>(result)); |
| }, |
| |
| .extended_features = |
| [](void* user, uint8_t features_page) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| return controller->GetLmpFeatures(features_page); |
| }, |
| |
| .send_hci_event = |
| [](void* user, const uint8_t* data, uintptr_t len) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| |
| auto event_code = static_cast<EventCode>(data[0]); |
| auto payload = std::make_unique<bluetooth::packet::RawBuilder>( |
| std::vector(data + 2, data + len)); |
| |
| controller->send_event_(bluetooth::hci::EventBuilder::Create( |
| event_code, std::move(payload))); |
| }, |
| |
| .send_lmp_packet = |
| [](void* user, const uint8_t(*to)[6], const uint8_t* data, |
| uintptr_t len) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| |
| auto payload = std::make_unique<bluetooth::packet::RawBuilder>( |
| std::vector(data, data + len)); |
| |
| Address source = controller->GetAddress(); |
| Address dest(*to); |
| |
| controller->SendLinkLayerPacket(model::packets::LmpBuilder::Create( |
| source, dest, std::move(payload))); |
| }}; |
| |
| lm_.reset(link_manager_create(ops_)); |
| } |
| |
| LinkLayerController::~LinkLayerController() {} |
| |
| void LinkLayerController::SendLeLinkLayerPacket( |
| std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet, |
| int8_t tx_power) { |
| std::shared_ptr<model::packets::LinkLayerPacketBuilder> shared_packet = |
| std::move(packet); |
| ScheduleTask(kNoDelayMs, [this, shared_packet, tx_power]() { |
| send_to_remote_(shared_packet, Phy::Type::LOW_ENERGY, tx_power); |
| }); |
| } |
| |
| void LinkLayerController::SendLinkLayerPacket( |
| std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet, |
| int8_t tx_power) { |
| std::shared_ptr<model::packets::LinkLayerPacketBuilder> shared_packet = |
| std::move(packet); |
| ScheduleTask(kNoDelayMs, [this, shared_packet, tx_power]() { |
| send_to_remote_(shared_packet, Phy::Type::BR_EDR, tx_power); |
| }); |
| } |
| |
| ErrorCode LinkLayerController::SendLeCommandToRemoteByAddress( |
| OpCode opcode, const Address& own_address, const Address& peer_address) { |
| switch (opcode) { |
| case (OpCode::LE_READ_REMOTE_FEATURES): |
| SendLeLinkLayerPacket(model::packets::LeReadRemoteFeaturesBuilder::Create( |
| own_address, peer_address)); |
| break; |
| default: |
| LOG_INFO("Dropping unhandled command 0x%04x", |
| static_cast<uint16_t>(opcode)); |
| return ErrorCode::UNKNOWN_HCI_COMMAND; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::SendCommandToRemoteByAddress( |
| OpCode opcode, bluetooth::packet::PacketView<true> args, |
| const Address& own_address, const Address& peer_address) { |
| switch (opcode) { |
| case (OpCode::REMOTE_NAME_REQUEST): |
| // LMP features get requested with remote name requests. |
| SendLinkLayerPacket(model::packets::ReadRemoteLmpFeaturesBuilder::Create( |
| own_address, peer_address)); |
| SendLinkLayerPacket(model::packets::RemoteNameRequestBuilder::Create( |
| own_address, peer_address)); |
| break; |
| case (OpCode::READ_REMOTE_SUPPORTED_FEATURES): |
| SendLinkLayerPacket( |
| model::packets::ReadRemoteSupportedFeaturesBuilder::Create( |
| own_address, peer_address)); |
| break; |
| case (OpCode::READ_REMOTE_EXTENDED_FEATURES): { |
| uint8_t page_number = |
| (args.begin() + 2).extract<uint8_t>(); // skip the handle |
| SendLinkLayerPacket( |
| model::packets::ReadRemoteExtendedFeaturesBuilder::Create( |
| own_address, peer_address, page_number)); |
| } break; |
| case (OpCode::READ_REMOTE_VERSION_INFORMATION): |
| SendLinkLayerPacket( |
| model::packets::ReadRemoteVersionInformationBuilder::Create( |
| own_address, peer_address)); |
| break; |
| case (OpCode::READ_CLOCK_OFFSET): |
| SendLinkLayerPacket(model::packets::ReadClockOffsetBuilder::Create( |
| own_address, peer_address)); |
| break; |
| default: |
| LOG_INFO("Dropping unhandled command 0x%04x", |
| static_cast<uint16_t>(opcode)); |
| return ErrorCode::UNKNOWN_HCI_COMMAND; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::SendCommandToRemoteByHandle( |
| OpCode opcode, bluetooth::packet::PacketView<true> args, uint16_t handle) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| switch (opcode) { |
| case (OpCode::LE_READ_REMOTE_FEATURES): |
| return SendLeCommandToRemoteByAddress( |
| opcode, connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress()); |
| default: |
| return SendCommandToRemoteByAddress( |
| opcode, args, connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress()); |
| } |
| } |
| |
| ErrorCode LinkLayerController::SendAclToRemote( |
| bluetooth::hci::AclView acl_packet) { |
| uint16_t handle = acl_packet.GetHandle(); |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| AddressWithType my_address = connections_.GetOwnAddress(handle); |
| AddressWithType destination = connections_.GetAddress(handle); |
| Phy::Type phy = connections_.GetPhyType(handle); |
| |
| auto acl_packet_payload = acl_packet.GetPayload(); |
| auto acl = model::packets::AclBuilder::Create( |
| my_address.GetAddress(), destination.GetAddress(), |
| static_cast<uint8_t>(acl_packet.GetPacketBoundaryFlag()), |
| static_cast<uint8_t>(acl_packet.GetBroadcastFlag()), |
| std::vector(acl_packet_payload.begin(), acl_packet_payload.end())); |
| |
| switch (phy) { |
| case Phy::Type::BR_EDR: |
| SendLinkLayerPacket(std::move(acl)); |
| break; |
| case Phy::Type::LOW_ENERGY: |
| SendLeLinkLayerPacket(std::move(acl)); |
| break; |
| } |
| |
| ScheduleTask(kNoDelayMs, [this, handle]() { |
| send_event_(bluetooth::hci::NumberOfCompletedPacketsBuilder::Create( |
| {bluetooth::hci::CompletedPackets(handle, 1)})); |
| }); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::SendScoToRemote( |
| bluetooth::hci::ScoView sco_packet) { |
| uint16_t handle = sco_packet.GetHandle(); |
| if (!connections_.HasScoHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // TODO: SCO flow control |
| Address source = GetAddress(); |
| Address destination = connections_.GetScoAddress(handle); |
| |
| auto sco_data = sco_packet.GetData(); |
| std::vector<uint8_t> sco_data_bytes(sco_data.begin(), sco_data.end()); |
| |
| SendLinkLayerPacket(model::packets::ScoBuilder::Create( |
| source, destination, |
| std::make_unique<bluetooth::packet::RawBuilder>(sco_data_bytes))); |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::IncomingPacket( |
| model::packets::LinkLayerPacketView incoming, int8_t rssi) { |
| ASSERT(incoming.IsValid()); |
| auto destination_address = incoming.GetDestinationAddress(); |
| |
| // Match broadcasts |
| bool address_matches = (destination_address == Address::kEmpty); |
| |
| // Address match is performed in specific handlers for these PDU types. |
| switch (incoming.GetType()) { |
| case model::packets::PacketType::LE_SCAN: |
| case model::packets::PacketType::LE_SCAN_RESPONSE: |
| case model::packets::PacketType::LE_LEGACY_ADVERTISING_PDU: |
| case model::packets::PacketType::LE_EXTENDED_ADVERTISING_PDU: |
| case model::packets::PacketType::LE_CONNECT: |
| address_matches = true; |
| break; |
| default: |
| break; |
| } |
| |
| // Check public address |
| if (destination_address == address_ || |
| destination_address == random_address_) { |
| address_matches = true; |
| } |
| |
| // Check current connection address |
| if (destination_address == initiator_.initiating_address) { |
| address_matches = true; |
| } |
| |
| // Check connection addresses |
| auto source_address = incoming.GetSourceAddress(); |
| auto handle = connections_.GetHandleOnlyAddress(source_address); |
| if (handle != kReservedHandle) { |
| if (connections_.GetOwnAddress(handle).GetAddress() == |
| destination_address) { |
| address_matches = true; |
| |
| // Update link timeout for valid ACL connections |
| connections_.ResetLinkTimer(handle); |
| } |
| } |
| |
| // Drop packets not addressed to me |
| if (!address_matches) { |
| LOG_INFO("%s | Dropping packet not addressed to me %s->%s (type 0x%x)", |
| address_.ToString().c_str(), source_address.ToString().c_str(), |
| destination_address.ToString().c_str(), |
| static_cast<int>(incoming.GetType())); |
| return; |
| } |
| |
| switch (incoming.GetType()) { |
| case model::packets::PacketType::ACL: |
| IncomingAclPacket(incoming, rssi); |
| break; |
| case model::packets::PacketType::SCO: |
| IncomingScoPacket(incoming); |
| break; |
| case model::packets::PacketType::DISCONNECT: |
| IncomingDisconnectPacket(incoming); |
| break; |
| case model::packets::PacketType::LMP: |
| IncomingLmpPacket(incoming); |
| break; |
| case model::packets::PacketType::INQUIRY: |
| if (inquiry_scan_enable_) { |
| IncomingInquiryPacket(incoming, rssi); |
| } |
| break; |
| case model::packets::PacketType::INQUIRY_RESPONSE: |
| IncomingInquiryResponsePacket(incoming); |
| break; |
| case PacketType::ISO: |
| IncomingIsoPacket(incoming); |
| break; |
| case PacketType::ISO_CONNECTION_REQUEST: |
| IncomingIsoConnectionRequestPacket(incoming); |
| break; |
| case PacketType::ISO_CONNECTION_RESPONSE: |
| IncomingIsoConnectionResponsePacket(incoming); |
| break; |
| case model::packets::PacketType::LE_LEGACY_ADVERTISING_PDU: |
| IncomingLeLegacyAdvertisingPdu(incoming, rssi); |
| return; |
| case model::packets::PacketType::LE_EXTENDED_ADVERTISING_PDU: |
| IncomingLeExtendedAdvertisingPdu(incoming, rssi); |
| return; |
| case model::packets::PacketType::LE_PERIODIC_ADVERTISING_PDU: |
| IncomingLePeriodicAdvertisingPdu(incoming, rssi); |
| return; |
| case model::packets::PacketType::LE_CONNECT: |
| IncomingLeConnectPacket(incoming); |
| break; |
| case model::packets::PacketType::LE_CONNECT_COMPLETE: |
| IncomingLeConnectCompletePacket(incoming); |
| break; |
| case model::packets::PacketType::LE_CONNECTION_PARAMETER_REQUEST: |
| IncomingLeConnectionParameterRequest(incoming); |
| break; |
| case model::packets::PacketType::LE_CONNECTION_PARAMETER_UPDATE: |
| IncomingLeConnectionParameterUpdate(incoming); |
| break; |
| case model::packets::PacketType::LE_ENCRYPT_CONNECTION: |
| IncomingLeEncryptConnection(incoming); |
| break; |
| case model::packets::PacketType::LE_ENCRYPT_CONNECTION_RESPONSE: |
| IncomingLeEncryptConnectionResponse(incoming); |
| break; |
| case (model::packets::PacketType::LE_READ_REMOTE_FEATURES): |
| IncomingLeReadRemoteFeatures(incoming); |
| break; |
| case (model::packets::PacketType::LE_READ_REMOTE_FEATURES_RESPONSE): |
| IncomingLeReadRemoteFeaturesResponse(incoming); |
| break; |
| case model::packets::PacketType::LE_SCAN: |
| IncomingLeScanPacket(incoming); |
| break; |
| case model::packets::PacketType::LE_SCAN_RESPONSE: |
| IncomingLeScanResponsePacket(incoming, rssi); |
| break; |
| case model::packets::PacketType::PAGE: |
| if (page_scan_enable_) { |
| IncomingPagePacket(incoming); |
| } |
| break; |
| case model::packets::PacketType::PAGE_RESPONSE: |
| IncomingPageResponsePacket(incoming); |
| break; |
| case model::packets::PacketType::PAGE_REJECT: |
| IncomingPageRejectPacket(incoming); |
| break; |
| case (model::packets::PacketType::REMOTE_NAME_REQUEST): |
| IncomingRemoteNameRequest(incoming); |
| break; |
| case (model::packets::PacketType::REMOTE_NAME_REQUEST_RESPONSE): |
| IncomingRemoteNameRequestResponse(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES): |
| IncomingReadRemoteSupportedFeatures(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES_RESPONSE): |
| IncomingReadRemoteSupportedFeaturesResponse(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES): |
| IncomingReadRemoteLmpFeatures(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES_RESPONSE): |
| IncomingReadRemoteLmpFeaturesResponse(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES): |
| IncomingReadRemoteExtendedFeatures(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES_RESPONSE): |
| IncomingReadRemoteExtendedFeaturesResponse(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION): |
| IncomingReadRemoteVersion(incoming); |
| break; |
| case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION_RESPONSE): |
| IncomingReadRemoteVersionResponse(incoming); |
| break; |
| case (model::packets::PacketType::READ_CLOCK_OFFSET): |
| IncomingReadClockOffset(incoming); |
| break; |
| case (model::packets::PacketType::READ_CLOCK_OFFSET_RESPONSE): |
| IncomingReadClockOffsetResponse(incoming); |
| break; |
| case model::packets::PacketType::SCO_CONNECTION_REQUEST: |
| IncomingScoConnectionRequest(incoming); |
| break; |
| case model::packets::PacketType::SCO_CONNECTION_RESPONSE: |
| IncomingScoConnectionResponse(incoming); |
| break; |
| case model::packets::PacketType::SCO_DISCONNECT: |
| IncomingScoDisconnect(incoming); |
| break; |
| case model::packets::PacketType::PING_REQUEST: |
| IncomingPingRequest(incoming); |
| break; |
| case model::packets::PacketType::PING_RESPONSE: |
| // ping responses require no action |
| break; |
| case model::packets::PacketType::ROLE_SWITCH_REQUEST: |
| IncomingRoleSwitchRequest(incoming); |
| break; |
| case model::packets::PacketType::ROLE_SWITCH_RESPONSE: |
| IncomingRoleSwitchResponse(incoming); |
| break; |
| case model::packets::PacketType::LL_PHY_REQ: |
| IncomingLlPhyReq(incoming); |
| break; |
| case model::packets::PacketType::LL_PHY_RSP: |
| IncomingLlPhyRsp(incoming); |
| break; |
| case model::packets::PacketType::LL_PHY_UPDATE_IND: |
| IncomingLlPhyUpdateInd(incoming); |
| break; |
| default: |
| LOG_WARN("Dropping unhandled packet of type %s", |
| model::packets::PacketTypeText(incoming.GetType()).c_str()); |
| } |
| } |
| |
| void LinkLayerController::IncomingAclPacket( |
| model::packets::LinkLayerPacketView incoming, int8_t rssi) { |
| auto acl = model::packets::AclView::Create(incoming); |
| ASSERT(acl.IsValid()); |
| |
| auto acl_data = acl.GetData(); |
| auto packet_boundary_flag = |
| bluetooth::hci::PacketBoundaryFlag(acl.GetPacketBoundaryFlag()); |
| auto broadcast_flag = bluetooth::hci::BroadcastFlag(acl.GetBroadcastFlag()); |
| |
| if (packet_boundary_flag == |
| bluetooth::hci::PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) { |
| packet_boundary_flag = |
| bluetooth::hci::PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE; |
| } |
| |
| LOG_INFO("Acl Packet [%zu] %s -> %s", acl_data.size(), |
| incoming.GetSourceAddress().ToString().c_str(), |
| incoming.GetDestinationAddress().ToString().c_str()); |
| |
| uint16_t connection_handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| if (connection_handle == kReservedHandle) { |
| LOG_INFO("Dropping packet since connection does not exist"); |
| return; |
| } |
| |
| // Update the RSSI for the local ACL connection. |
| connections_.SetRssi(connection_handle, rssi); |
| |
| // Send the packet to the host segmented according to the |
| // controller ACL data packet length. |
| size_t acl_buffer_size = properties_.acl_data_packet_length; |
| size_t offset = 0; |
| |
| while (offset < acl_data.size()) { |
| size_t fragment_size = std::min(acl_buffer_size, acl_data.size() - offset); |
| std::vector<uint8_t> fragment(acl_data.begin() + offset, |
| acl_data.begin() + offset + fragment_size); |
| |
| auto acl_packet = bluetooth::hci::AclBuilder::Create( |
| connection_handle, packet_boundary_flag, broadcast_flag, |
| std::make_unique<bluetooth::packet::RawBuilder>(std::move(fragment))); |
| |
| send_acl_(std::move(acl_packet)); |
| |
| packet_boundary_flag = |
| bluetooth::hci::PacketBoundaryFlag::CONTINUING_FRAGMENT; |
| offset += fragment_size; |
| } |
| } |
| |
| void LinkLayerController::IncomingScoPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| Address source = incoming.GetSourceAddress(); |
| uint16_t sco_handle = connections_.GetScoHandle(source); |
| if (!connections_.HasScoHandle(sco_handle)) { |
| LOG_INFO("Spurious SCO packet from %s", source.ToString().c_str()); |
| return; |
| } |
| |
| auto sco = model::packets::ScoView::Create(incoming); |
| ASSERT(sco.IsValid()); |
| auto sco_data = sco.GetPayload(); |
| std::vector<uint8_t> sco_data_bytes(sco_data.begin(), sco_data.end()); |
| |
| LOG_INFO("Sco Packet [%d] %s -> %s", static_cast<int>(sco_data_bytes.size()), |
| incoming.GetSourceAddress().ToString().c_str(), |
| incoming.GetDestinationAddress().ToString().c_str()); |
| |
| send_sco_(bluetooth::hci::ScoBuilder::Create( |
| sco_handle, bluetooth::hci::PacketStatusFlag::CORRECTLY_RECEIVED, |
| sco_data_bytes)); |
| } |
| |
| void LinkLayerController::IncomingRemoteNameRequest( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = model::packets::RemoteNameRequestView::Create(incoming); |
| ASSERT(view.IsValid()); |
| |
| SendLinkLayerPacket(model::packets::RemoteNameRequestResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| local_name_)); |
| } |
| |
| void LinkLayerController::IncomingRemoteNameRequestResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = model::packets::RemoteNameRequestResponseView::Create(incoming); |
| ASSERT(view.IsValid()); |
| |
| if (IsEventUnmasked(EventCode::REMOTE_NAME_REQUEST_COMPLETE)) { |
| send_event_(bluetooth::hci::RemoteNameRequestCompleteBuilder::Create( |
| ErrorCode::SUCCESS, incoming.GetSourceAddress(), view.GetName())); |
| } |
| } |
| |
| void LinkLayerController::IncomingReadRemoteLmpFeatures( |
| model::packets::LinkLayerPacketView incoming) { |
| SendLinkLayerPacket( |
| model::packets::ReadRemoteLmpFeaturesResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| host_supported_features_)); |
| } |
| |
| void LinkLayerController::IncomingReadRemoteLmpFeaturesResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = |
| model::packets::ReadRemoteLmpFeaturesResponseView::Create(incoming); |
| ASSERT(view.IsValid()); |
| if (IsEventUnmasked(EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION)) { |
| send_event_( |
| bluetooth::hci::RemoteHostSupportedFeaturesNotificationBuilder::Create( |
| incoming.GetSourceAddress(), view.GetFeatures())); |
| } |
| } |
| |
| void LinkLayerController::IncomingReadRemoteSupportedFeatures( |
| model::packets::LinkLayerPacketView incoming) { |
| SendLinkLayerPacket( |
| model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| properties_.lmp_features[0])); |
| } |
| |
| void LinkLayerController::IncomingReadRemoteSupportedFeaturesResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = |
| model::packets::ReadRemoteSupportedFeaturesResponseView::Create(incoming); |
| ASSERT(view.IsValid()); |
| Address source = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(source); |
| if (handle == kReservedHandle) { |
| LOG_INFO("Discarding response from a disconnected device %s", |
| source.ToString().c_str()); |
| return; |
| } |
| if (IsEventUnmasked(EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE)) { |
| send_event_( |
| bluetooth::hci::ReadRemoteSupportedFeaturesCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, view.GetFeatures())); |
| } |
| } |
| |
| void LinkLayerController::IncomingReadRemoteExtendedFeatures( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = model::packets::ReadRemoteExtendedFeaturesView::Create(incoming); |
| ASSERT(view.IsValid()); |
| uint8_t page_number = view.GetPageNumber(); |
| uint8_t error_code = static_cast<uint8_t>(ErrorCode::SUCCESS); |
| if (page_number >= properties_.lmp_features.size()) { |
| error_code = static_cast<uint8_t>(ErrorCode::INVALID_LMP_OR_LL_PARAMETERS); |
| } |
| SendLinkLayerPacket( |
| model::packets::ReadRemoteExtendedFeaturesResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| error_code, page_number, GetMaxLmpFeaturesPageNumber(), |
| GetLmpFeatures(page_number))); |
| } |
| |
| void LinkLayerController::IncomingReadRemoteExtendedFeaturesResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = |
| model::packets::ReadRemoteExtendedFeaturesResponseView::Create(incoming); |
| ASSERT(view.IsValid()); |
| Address source = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(source); |
| if (handle == kReservedHandle) { |
| LOG_INFO("Discarding response from a disconnected device %s", |
| source.ToString().c_str()); |
| return; |
| } |
| if (IsEventUnmasked(EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE)) { |
| send_event_( |
| bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create( |
| static_cast<ErrorCode>(view.GetStatus()), handle, |
| view.GetPageNumber(), view.GetMaxPageNumber(), view.GetFeatures())); |
| } |
| } |
| |
| void LinkLayerController::IncomingReadRemoteVersion( |
| model::packets::LinkLayerPacketView incoming) { |
| SendLinkLayerPacket( |
| model::packets::ReadRemoteVersionInformationResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| static_cast<uint8_t>(properties_.lmp_version), |
| static_cast<uint16_t>(properties_.lmp_subversion), |
| properties_.company_identifier)); |
| } |
| |
| void LinkLayerController::IncomingReadRemoteVersionResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = model::packets::ReadRemoteVersionInformationResponseView::Create( |
| incoming); |
| ASSERT(view.IsValid()); |
| Address source = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(source); |
| if (handle == kReservedHandle) { |
| LOG_INFO("Discarding response from a disconnected device %s", |
| source.ToString().c_str()); |
| return; |
| } |
| if (IsEventUnmasked(EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE)) { |
| send_event_( |
| bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, view.GetLmpVersion(), |
| view.GetManufacturerName(), view.GetLmpSubversion())); |
| } |
| } |
| |
| void LinkLayerController::IncomingReadClockOffset( |
| model::packets::LinkLayerPacketView incoming) { |
| SendLinkLayerPacket(model::packets::ReadClockOffsetResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| GetClockOffset())); |
| } |
| |
| void LinkLayerController::IncomingReadClockOffsetResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = model::packets::ReadClockOffsetResponseView::Create(incoming); |
| ASSERT(view.IsValid()); |
| Address source = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(source); |
| if (handle == kReservedHandle) { |
| LOG_INFO("Discarding response from a disconnected device %s", |
| source.ToString().c_str()); |
| return; |
| } |
| if (IsEventUnmasked(EventCode::READ_CLOCK_OFFSET_COMPLETE)) { |
| send_event_(bluetooth::hci::ReadClockOffsetCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, view.GetOffset())); |
| } |
| } |
| |
| void LinkLayerController::IncomingDisconnectPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| LOG_INFO("Disconnect Packet"); |
| auto disconnect = model::packets::DisconnectView::Create(incoming); |
| ASSERT(disconnect.IsValid()); |
| |
| Address peer = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(peer); |
| if (handle == kReservedHandle) { |
| LOG_INFO("Discarding disconnect from a disconnected device %s", |
| peer.ToString().c_str()); |
| return; |
| } |
| auto is_br_edr = connections_.GetPhyType(handle) == Phy::Type::BR_EDR; |
| ASSERT_LOG( |
| connections_.Disconnect( |
| handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }), |
| "GetHandle() returned invalid handle %hx", handle); |
| |
| uint8_t reason = disconnect.GetReason(); |
| SendDisconnectionCompleteEvent(handle, ErrorCode(reason)); |
| if (is_br_edr) { |
| ASSERT(link_manager_remove_link( |
| lm_.get(), reinterpret_cast<uint8_t(*)[6]>(peer.data()))); |
| } |
| } |
| |
| void LinkLayerController::IncomingInquiryPacket( |
| model::packets::LinkLayerPacketView incoming, uint8_t rssi) { |
| auto inquiry = model::packets::InquiryView::Create(incoming); |
| ASSERT(inquiry.IsValid()); |
| |
| Address peer = incoming.GetSourceAddress(); |
| uint8_t lap = inquiry.GetLap(); |
| |
| // Filter out inquiry packets with IAC not present in the |
| // list Current_IAC_LAP. |
| if (std::none_of(current_iac_lap_list_.cbegin(), current_iac_lap_list_.cend(), |
| [lap](auto iac_lap) { return iac_lap.lap_ == lap; })) { |
| return; |
| } |
| |
| switch (inquiry.GetInquiryType()) { |
| case (model::packets::InquiryType::STANDARD): { |
| SendLinkLayerPacket(model::packets::InquiryResponseBuilder::Create( |
| GetAddress(), peer, static_cast<uint8_t>(GetPageScanRepetitionMode()), |
| class_of_device_, GetClockOffset())); |
| } break; |
| case (model::packets::InquiryType::RSSI): { |
| SendLinkLayerPacket( |
| model::packets::InquiryResponseWithRssiBuilder::Create( |
| GetAddress(), peer, |
| static_cast<uint8_t>(GetPageScanRepetitionMode()), |
| class_of_device_, GetClockOffset(), rssi)); |
| } break; |
| case (model::packets::InquiryType::EXTENDED): { |
| SendLinkLayerPacket( |
| model::packets::ExtendedInquiryResponseBuilder::Create( |
| GetAddress(), peer, |
| static_cast<uint8_t>(GetPageScanRepetitionMode()), |
| class_of_device_, GetClockOffset(), rssi, |
| extended_inquiry_response_)); |
| |
| } break; |
| default: |
| LOG_WARN("Unhandled Incoming Inquiry of type %d", |
| static_cast<int>(inquiry.GetType())); |
| return; |
| } |
| // TODO: Send an Inquiry Response Notification Event 7.7.74 |
| } |
| |
| void LinkLayerController::IncomingInquiryResponsePacket( |
| model::packets::LinkLayerPacketView incoming) { |
| auto basic_inquiry_response = |
| model::packets::BasicInquiryResponseView::Create(incoming); |
| ASSERT(basic_inquiry_response.IsValid()); |
| std::vector<uint8_t> eir; |
| |
| switch (basic_inquiry_response.GetInquiryType()) { |
| case (model::packets::InquiryType::STANDARD): { |
| // TODO: Support multiple inquiries in the same packet. |
| auto inquiry_response = |
| model::packets::InquiryResponseView::Create(basic_inquiry_response); |
| ASSERT(inquiry_response.IsValid()); |
| |
| auto page_scan_repetition_mode = |
| (bluetooth::hci::PageScanRepetitionMode) |
| inquiry_response.GetPageScanRepetitionMode(); |
| |
| std::vector<bluetooth::hci::InquiryResponse> responses; |
| responses.emplace_back(); |
| responses.back().bd_addr_ = inquiry_response.GetSourceAddress(); |
| responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode; |
| responses.back().class_of_device_ = inquiry_response.GetClassOfDevice(); |
| responses.back().clock_offset_ = inquiry_response.GetClockOffset(); |
| if (IsEventUnmasked(EventCode::INQUIRY_RESULT)) { |
| send_event_(bluetooth::hci::InquiryResultBuilder::Create(responses)); |
| } |
| } break; |
| |
| case (model::packets::InquiryType::RSSI): { |
| auto inquiry_response = |
| model::packets::InquiryResponseWithRssiView::Create( |
| basic_inquiry_response); |
| ASSERT(inquiry_response.IsValid()); |
| |
| auto page_scan_repetition_mode = |
| (bluetooth::hci::PageScanRepetitionMode) |
| inquiry_response.GetPageScanRepetitionMode(); |
| |
| std::vector<bluetooth::hci::InquiryResponseWithRssi> responses; |
| responses.emplace_back(); |
| responses.back().address_ = inquiry_response.GetSourceAddress(); |
| responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode; |
| responses.back().class_of_device_ = inquiry_response.GetClassOfDevice(); |
| responses.back().clock_offset_ = inquiry_response.GetClockOffset(); |
| responses.back().rssi_ = inquiry_response.GetRssi(); |
| if (IsEventUnmasked(EventCode::INQUIRY_RESULT_WITH_RSSI)) { |
| send_event_( |
| bluetooth::hci::InquiryResultWithRssiBuilder::Create(responses)); |
| } |
| } break; |
| |
| case (model::packets::InquiryType::EXTENDED): { |
| auto inquiry_response = |
| model::packets::ExtendedInquiryResponseView::Create( |
| basic_inquiry_response); |
| ASSERT(inquiry_response.IsValid()); |
| |
| send_event_(bluetooth::hci::ExtendedInquiryResultRawBuilder::Create( |
| inquiry_response.GetSourceAddress(), |
| static_cast<bluetooth::hci::PageScanRepetitionMode>( |
| inquiry_response.GetPageScanRepetitionMode()), |
| inquiry_response.GetClassOfDevice(), |
| inquiry_response.GetClockOffset(), inquiry_response.GetRssi(), |
| extended_inquiry_response_)); |
| } break; |
| default: |
| LOG_WARN("Unhandled Incoming Inquiry Response of type %d", |
| static_cast<int>(basic_inquiry_response.GetInquiryType())); |
| } |
| } |
| |
| void LinkLayerController::IncomingIsoPacket(LinkLayerPacketView incoming) { |
| auto iso = IsoDataPacketView::Create(incoming); |
| ASSERT(iso.IsValid()); |
| |
| uint16_t cis_handle = iso.GetHandle(); |
| if (!connections_.HasCisHandle(cis_handle)) { |
| LOG_INFO("Dropping ISO packet to unknown handle 0x%hx", cis_handle); |
| return; |
| } |
| if (!connections_.HasConnectedCis(cis_handle)) { |
| LOG_INFO("Dropping ISO packet to a disconnected handle 0x%hx", cis_handle); |
| return; |
| } |
| |
| auto sc = iso.GetSc(); |
| switch (sc) { |
| case StartContinuation::START: { |
| auto iso_start = IsoStartView::Create(iso); |
| ASSERT(iso_start.IsValid()); |
| if (iso.GetCmplt() == Complete::COMPLETE) { |
| send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create( |
| cis_handle, bluetooth::hci::IsoPacketBoundaryFlag::COMPLETE_SDU, |
| 0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID, |
| std::make_unique<bluetooth::packet::RawBuilder>( |
| std::vector<uint8_t>(iso_start.GetPayload().begin(), |
| iso_start.GetPayload().end())))); |
| } else { |
| send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create( |
| cis_handle, bluetooth::hci::IsoPacketBoundaryFlag::FIRST_FRAGMENT, |
| 0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID, |
| std::make_unique<bluetooth::packet::RawBuilder>( |
| std::vector<uint8_t>(iso_start.GetPayload().begin(), |
| iso_start.GetPayload().end())))); |
| } |
| } break; |
| case StartContinuation::CONTINUATION: { |
| auto continuation = IsoContinuationView::Create(iso); |
| ASSERT(continuation.IsValid()); |
| if (iso.GetCmplt() == Complete::COMPLETE) { |
| send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create( |
| cis_handle, bluetooth::hci::IsoPacketBoundaryFlag::LAST_FRAGMENT, |
| 0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID, |
| std::make_unique<bluetooth::packet::RawBuilder>( |
| std::vector<uint8_t>(continuation.GetPayload().begin(), |
| continuation.GetPayload().end())))); |
| } else { |
| send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create( |
| cis_handle, |
| bluetooth::hci::IsoPacketBoundaryFlag::CONTINUATION_FRAGMENT, |
| 0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID, |
| std::make_unique<bluetooth::packet::RawBuilder>( |
| std::vector<uint8_t>(continuation.GetPayload().begin(), |
| continuation.GetPayload().end())))); |
| } |
| } break; |
| } |
| } |
| |
| void LinkLayerController::HandleIso(bluetooth::hci::IsoView iso) { |
| auto cis_handle = iso.GetConnectionHandle(); |
| if (!connections_.HasCisHandle(cis_handle)) { |
| LOG_INFO("Dropping ISO packet to unknown handle 0x%hx", cis_handle); |
| return; |
| } |
| if (!connections_.HasConnectedCis(cis_handle)) { |
| LOG_INFO("Dropping ISO packet to disconnected handle 0x%hx", cis_handle); |
| return; |
| } |
| |
| auto acl_handle = connections_.GetAclHandleForCisHandle(cis_handle); |
| uint16_t remote_handle = |
| connections_.GetRemoteCisHandleForCisHandle(cis_handle); |
| model::packets::StartContinuation start_flag = |
| model::packets::StartContinuation::START; |
| model::packets::Complete complete_flag = model::packets::Complete::COMPLETE; |
| switch (iso.GetPbFlag()) { |
| case bluetooth::hci::IsoPacketBoundaryFlag::COMPLETE_SDU: |
| start_flag = model::packets::StartContinuation::START; |
| complete_flag = model::packets::Complete::COMPLETE; |
| break; |
| case bluetooth::hci::IsoPacketBoundaryFlag::CONTINUATION_FRAGMENT: |
| start_flag = model::packets::StartContinuation::CONTINUATION; |
| complete_flag = model::packets::Complete::INCOMPLETE; |
| break; |
| case bluetooth::hci::IsoPacketBoundaryFlag::FIRST_FRAGMENT: |
| start_flag = model::packets::StartContinuation::START; |
| complete_flag = model::packets::Complete::INCOMPLETE; |
| break; |
| case bluetooth::hci::IsoPacketBoundaryFlag::LAST_FRAGMENT: |
| start_flag = model::packets::StartContinuation::CONTINUATION; |
| complete_flag = model::packets::Complete::INCOMPLETE; |
| break; |
| } |
| if (start_flag == model::packets::StartContinuation::START) { |
| if (iso.GetTsFlag() == bluetooth::hci::TimeStampFlag::PRESENT) { |
| auto timestamped = bluetooth::hci::IsoWithTimestampView::Create(iso); |
| ASSERT(timestamped.IsValid()); |
| uint32_t timestamp = timestamped.GetTimeStamp(); |
| std::vector<uint8_t> payload; |
| for (const auto it : timestamped.GetPayload()) { |
| payload.push_back(it); |
| } |
| |
| SendLeLinkLayerPacket(model::packets::IsoStartBuilder::Create( |
| connections_.GetOwnAddress(acl_handle).GetAddress(), |
| connections_.GetAddress(acl_handle).GetAddress(), remote_handle, |
| complete_flag, timestamp, |
| std::make_unique<bluetooth::packet::RawBuilder>(std::move(payload)))); |
| } else { |
| auto pkt = bluetooth::hci::IsoWithoutTimestampView::Create(iso); |
| ASSERT(pkt.IsValid()); |
| |
| auto payload = |
| std::make_unique<bluetooth::packet::RawBuilder>(std::vector<uint8_t>( |
| pkt.GetPayload().begin(), pkt.GetPayload().end())); |
| |
| SendLeLinkLayerPacket(model::packets::IsoStartBuilder::Create( |
| connections_.GetOwnAddress(acl_handle).GetAddress(), |
| connections_.GetAddress(acl_handle).GetAddress(), remote_handle, |
| complete_flag, 0, std::move(payload))); |
| } |
| } else { |
| auto pkt = bluetooth::hci::IsoWithoutTimestampView::Create(iso); |
| ASSERT(pkt.IsValid()); |
| auto payload = std::make_unique<bluetooth::packet::RawBuilder>( |
| std::vector<uint8_t>(pkt.GetPayload().begin(), pkt.GetPayload().end())); |
| SendLeLinkLayerPacket(model::packets::IsoContinuationBuilder::Create( |
| connections_.GetOwnAddress(acl_handle).GetAddress(), |
| connections_.GetAddress(acl_handle).GetAddress(), remote_handle, |
| complete_flag, std::move(payload))); |
| } |
| } |
| |
| void LinkLayerController::IncomingIsoConnectionRequestPacket( |
| LinkLayerPacketView incoming) { |
| auto req = IsoConnectionRequestView::Create(incoming); |
| ASSERT(req.IsValid()); |
| std::vector<bluetooth::hci::CisParametersConfig> stream_configs; |
| bluetooth::hci::CisParametersConfig stream_config; |
| |
| stream_config.max_sdu_m_to_s_ = req.GetMaxSduMToS(); |
| stream_config.max_sdu_s_to_m_ = req.GetMaxSduSToM(); |
| |
| stream_configs.push_back(stream_config); |
| |
| uint8_t group_id = req.GetCigId(); |
| |
| /* CIG should be created by the local host before use */ |
| bluetooth::hci::CreateCisConfig config; |
| config.cis_connection_handle_ = req.GetRequesterCisHandle(); |
| |
| config.acl_connection_handle_ = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| connections_.CreatePendingCis(config); |
| connections_.SetRemoteCisHandle(config.cis_connection_handle_, |
| req.GetRequesterCisHandle()); |
| if (IsEventUnmasked(EventCode::LE_META_EVENT)) { |
| send_event_(bluetooth::hci::LeCisRequestBuilder::Create( |
| config.acl_connection_handle_, config.cis_connection_handle_, group_id, |
| req.GetId())); |
| } |
| } |
| |
| void LinkLayerController::IncomingIsoConnectionResponsePacket( |
| LinkLayerPacketView incoming) { |
| auto response = IsoConnectionResponseView::Create(incoming); |
| ASSERT(response.IsValid()); |
| |
| bluetooth::hci::CreateCisConfig config; |
| config.acl_connection_handle_ = response.GetRequesterAclHandle(); |
| config.cis_connection_handle_ = response.GetRequesterCisHandle(); |
| if (!connections_.HasPendingCisConnection(config.cis_connection_handle_)) { |
| LOG_INFO("Ignoring connection response with unknown CIS handle 0x%04hx", |
| config.cis_connection_handle_); |
| return; |
| } |
| ErrorCode status = static_cast<ErrorCode>(response.GetStatus()); |
| if (status != ErrorCode::SUCCESS) { |
| if (IsEventUnmasked(EventCode::LE_META_EVENT)) { |
| send_event_(bluetooth::hci::LeCisEstablishedBuilder::Create( |
| status, config.cis_connection_handle_, 0, 0, 0, 0, |
| bluetooth::hci::SecondaryPhyType::NO_PACKETS, |
| bluetooth::hci::SecondaryPhyType::NO_PACKETS, 0, 0, 0, 0, 0, 0, 0, |
| 0)); |
| } |
| return; |
| } |
| connections_.SetRemoteCisHandle(config.cis_connection_handle_, |
| response.GetResponderCisHandle()); |
| connections_.ConnectCis(config.cis_connection_handle_); |
| auto stream_parameters = |
| connections_.GetStreamParameters(config.cis_connection_handle_); |
| auto group_parameters = |
| connections_.GetGroupParameters(stream_parameters.group_id); |
| // TODO: Which of these are important enough to fake? |
| uint32_t cig_sync_delay = 0x100; |
| uint32_t cis_sync_delay = 0x200; |
| uint32_t latency_m_to_s = group_parameters.max_transport_latency_m_to_s; |
| uint32_t latency_s_to_m = group_parameters.max_transport_latency_s_to_m; |
| uint8_t nse = 1; |
| uint8_t bn_m_to_s = 0; |
| uint8_t bn_s_to_m = 0; |
| uint8_t ft_m_to_s = 0; |
| uint8_t ft_s_to_m = 0; |
| uint8_t max_pdu_m_to_s = 0x40; |
| uint8_t max_pdu_s_to_m = 0x40; |
| uint16_t iso_interval = 0x100; |
| if (IsEventUnmasked(EventCode::LE_META_EVENT)) { |
| send_event_(bluetooth::hci::LeCisEstablishedBuilder::Create( |
| status, config.cis_connection_handle_, cig_sync_delay, cis_sync_delay, |
| latency_m_to_s, latency_s_to_m, |
| bluetooth::hci::SecondaryPhyType::NO_PACKETS, |
| bluetooth::hci::SecondaryPhyType::NO_PACKETS, nse, bn_m_to_s, bn_s_to_m, |
| ft_m_to_s, ft_s_to_m, max_pdu_m_to_s, max_pdu_s_to_m, iso_interval)); |
| } |
| } |
| |
| Address LinkLayerController::generate_rpa( |
| std::array<uint8_t, LinkLayerController::kIrkSize> irk) { |
| // most significant bit, bit7, bit6 is 01 to be resolvable random |
| // Bits of the random part of prand shall not be all 1 or all 0 |
| std::array<uint8_t, 3> prand; |
| prand[0] = std::rand(); |
| prand[1] = std::rand(); |
| prand[2] = std::rand(); |
| |
| constexpr uint8_t BLE_RESOLVE_ADDR_MSB = 0x40; |
| prand[2] &= ~0xC0; // BLE Address mask |
| if ((prand[0] == 0x00 && prand[1] == 0x00 && prand[2] == 0x00) || |
| (prand[0] == 0xFF && prand[1] == 0xFF && prand[2] == 0x3F)) { |
| prand[0] = (uint8_t)(std::rand() % 0xFE + 1); |
| } |
| prand[2] |= BLE_RESOLVE_ADDR_MSB; |
| |
| Address rpa; |
| rpa.address[3] = prand[0]; |
| rpa.address[4] = prand[1]; |
| rpa.address[5] = prand[2]; |
| |
| /* encrypt with IRK */ |
| rootcanal::crypto::Octet16 p = |
| rootcanal::crypto::aes_128(irk, prand.data(), 3); |
| |
| /* set hash to be LSB of rpAddress */ |
| rpa.address[0] = p[0]; |
| rpa.address[1] = p[1]; |
| rpa.address[2] = p[2]; |
| LOG_INFO("RPA %s", rpa.ToString().c_str()); |
| return rpa; |
| } |
| |
| // Handle legacy advertising PDUs while in the Scanning state. |
| void LinkLayerController::ScanIncomingLeLegacyAdvertisingPdu( |
| model::packets::LeLegacyAdvertisingPduView& pdu, uint8_t rssi) { |
| if (!scanner_.IsEnabled()) { |
| return; |
| } |
| |
| auto advertising_type = pdu.GetAdvertisingType(); |
| std::vector<uint8_t> advertising_data = pdu.GetAdvertisingData(); |
| |
| AddressWithType advertising_address{ |
| pdu.GetSourceAddress(), |
| static_cast<AddressType>(pdu.GetAdvertisingAddressType())}; |
| |
| AddressWithType target_address{ |
| pdu.GetDestinationAddress(), |
| static_cast<AddressType>(pdu.GetTargetAddressType())}; |
| |
| bool scannable_advertising = |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_IND || |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_SCAN_IND; |
| |
| bool directed_advertising = |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND; |
| |
| bool connectable_advertising = |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_IND || |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND; |
| |
| // TODO: check originating PHY, compare against active scanning PHYs |
| // (scanner_.le_1m_phy or scanner_.le_coded_phy). |
| |
| // When a scanner receives an advertising packet that contains a resolvable |
| // private address for the advertiser’s device address (AdvA field) and |
| // address resolution is enabled, the Link Layer shall resolve the private |
| // address. The scanner’s filter policy shall then determine if the scanner |
| // responds with a scan request. |
| AddressWithType resolved_advertising_address = |
| ResolvePrivateAddress(advertising_address, IrkSelection::Peer) |
| .value_or(advertising_address); |
| |
| std::optional<AddressWithType> resolved_target_address = |
| ResolvePrivateAddress(target_address, IrkSelection::Peer); |
| |
| if (resolved_advertising_address != advertising_address) { |
| LOG_VERB("Resolved the advertising address %s(%hhx) to %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| resolved_advertising_address.ToString().c_str(), |
| resolved_advertising_address.GetAddressType()); |
| } |
| |
| // Vol 6, Part B § 4.3.3 Scanner filter policy |
| switch (scanner_.scan_filter_policy) { |
| case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL: |
| case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY: |
| break; |
| case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY: |
| case bluetooth::hci::LeScanningFilterPolicy:: |
| FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY: |
| if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) { |
| LOG_VERB( |
| "Legacy advertising ignored by scanner because the advertising " |
| "address %s(%hhx) is not in the filter accept list", |
| resolved_advertising_address.ToString().c_str(), |
| resolved_advertising_address.GetAddressType()); |
| return; |
| } |
| break; |
| } |
| |
| // When LE_Set_Scan_Enable is used: |
| // |
| // When the Scanning_Filter_Policy is set to 0x02 or 0x03 (see Section 7.8.10) |
| // and a directed advertisement was received where the advertiser used a |
| // resolvable private address which the Controller is unable to resolve, an |
| // HCI_LE_Directed_Advertising_Report event shall be generated instead of an |
| // HCI_LE_Advertising_Report event. |
| bool should_send_directed_advertising_report = false; |
| |
| if (directed_advertising) { |
| switch (scanner_.scan_filter_policy) { |
| // In both basic scanner filter policy modes, a directed advertising PDU |
| // shall be ignored unless either: |
| // • the TargetA field is identical to the scanner's device address, or |
| // • the TargetA field is a resolvable private address, address |
| // resolution is enabled, and the address is resolved successfully |
| case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL: |
| case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY: |
| if (!IsLocalPublicOrRandomAddress(target_address) && |
| !(target_address.IsRpa() && resolved_target_address)) { |
| LOG_VERB( |
| "Legacy advertising ignored by scanner because the directed " |
| "address %s(%hhx) does not match the current device or cannot be " |
| "resolved", |
| target_address.ToString().c_str(), |
| target_address.GetAddressType()); |
| return; |
| } |
| break; |
| // These are identical to the basic modes except |
| // that a directed advertising PDU shall be ignored unless either: |
| // • the TargetA field is identical to the scanner's device address, or |
| // • the TargetA field is a resolvable private address. |
| case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY: |
| case bluetooth::hci::LeScanningFilterPolicy:: |
| FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY: |
| if (!IsLocalPublicOrRandomAddress(target_address) && |
| !target_address.IsRpa()) { |
| LOG_VERB( |
| "Legacy advertising ignored by scanner because the directed " |
| "address %s(%hhx) does not match the current device or is not a " |
| "resovable private address", |
| target_address.ToString().c_str(), |
| target_address.GetAddressType()); |
| return; |
| } |
| should_send_directed_advertising_report = |
| target_address.IsRpa() && !resolved_target_address; |
| break; |
| } |
| } |
| |
| bool should_send_advertising_report = true; |
| if (scanner_.filter_duplicates != |
| bluetooth::hci::FilterDuplicates::DISABLED) { |
| if (scanner_.IsPacketInHistory(pdu)) { |
| should_send_advertising_report = false; |
| } else { |
| scanner_.AddPacketToHistory(pdu); |
| } |
| } |
| |
| // Legacy scanning, directed advertising. |
| if (LegacyAdvertising() && should_send_advertising_report && |
| should_send_directed_advertising_report && |
| IsLeEventUnmasked(SubeventCode::DIRECTED_ADVERTISING_REPORT)) { |
| bluetooth::hci::LeDirectedAdvertisingResponse response; |
| response.event_type_ = |
| bluetooth::hci::DirectAdvertisingEventType::ADV_DIRECT_IND; |
| response.address_type_ = |
| static_cast<bluetooth::hci::DirectAdvertisingAddressType>( |
| resolved_advertising_address.GetAddressType()); |
| response.address_ = resolved_advertising_address.GetAddress(); |
| response.direct_address_type_ = |
| bluetooth::hci::DirectAddressType::RANDOM_DEVICE_ADDRESS; |
| response.direct_address_ = target_address.GetAddress(); |
| response.rssi_ = rssi; |
| |
| send_event_( |
| bluetooth::hci::LeDirectedAdvertisingReportBuilder::Create({response})); |
| } |
| |
| // Legacy scanning, un-directed advertising. |
| if (LegacyAdvertising() && should_send_advertising_report && |
| !should_send_directed_advertising_report && |
| IsLeEventUnmasked(SubeventCode::ADVERTISING_REPORT)) { |
| bluetooth::hci::LeAdvertisingResponseRaw response; |
| response.address_type_ = resolved_advertising_address.GetAddressType(); |
| response.address_ = resolved_advertising_address.GetAddress(); |
| response.advertising_data_ = advertising_data; |
| response.rssi_ = rssi; |
| |
| switch (advertising_type) { |
| case model::packets::LegacyAdvertisingType::ADV_IND: |
| response.event_type_ = bluetooth::hci::AdvertisingEventType::ADV_IND; |
| break; |
| case model::packets::LegacyAdvertisingType::ADV_DIRECT_IND: |
| response.event_type_ = |
| bluetooth::hci::AdvertisingEventType::ADV_DIRECT_IND; |
| break; |
| case model::packets::LegacyAdvertisingType::ADV_SCAN_IND: |
| response.event_type_ = |
| bluetooth::hci::AdvertisingEventType::ADV_SCAN_IND; |
| break; |
| case model::packets::LegacyAdvertisingType::ADV_NONCONN_IND: |
| response.event_type_ = |
| bluetooth::hci::AdvertisingEventType::ADV_NONCONN_IND; |
| break; |
| } |
| |
| send_event_( |
| bluetooth::hci::LeAdvertisingReportRawBuilder::Create({response})); |
| } |
| |
| // Extended scanning. |
| if (ExtendedAdvertising() && should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) { |
| bluetooth::hci::LeExtendedAdvertisingResponseRaw response; |
| response.connectable_ = connectable_advertising; |
| response.scannable_ = scannable_advertising; |
| response.directed_ = directed_advertising; |
| response.scan_response_ = false; |
| response.legacy_ = true; |
| response.data_status_ = bluetooth::hci::DataStatus::COMPLETE; |
| response.address_type_ = |
| static_cast<bluetooth::hci::DirectAdvertisingAddressType>( |
| resolved_advertising_address.GetAddressType()); |
| response.address_ = resolved_advertising_address.GetAddress(); |
| response.primary_phy_ = bluetooth::hci::PrimaryPhyType::LE_1M; |
| response.secondary_phy_ = bluetooth::hci::SecondaryPhyType::NO_PACKETS; |
| response.advertising_sid_ = 0xff; // ADI not provided. |
| response.tx_power_ = 0x7f; // TX power information not available. |
| response.rssi_ = rssi; |
| response.periodic_advertising_interval_ = 0; // No periodic advertising. |
| if (directed_advertising) { |
| response.direct_address_type_ = |
| bluetooth::hci::DirectAdvertisingAddressType( |
| target_address.GetAddressType()); |
| response.direct_address_ = target_address.GetAddress(); |
| } else { |
| response.direct_address_type_ = |
| bluetooth::hci::DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED; |
| response.direct_address_ = Address::kEmpty; |
| } |
| response.advertising_data_ = advertising_data; |
| |
| send_event_(bluetooth::hci::LeExtendedAdvertisingReportRawBuilder::Create( |
| {response})); |
| } |
| |
| // Did the user enable Active scanning ? |
| bool active_scanning = |
| (scanner_.le_1m_phy.enabled && |
| scanner_.le_1m_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE) || |
| (scanner_.le_coded_phy.enabled && |
| scanner_.le_coded_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE); |
| |
| // Active scanning. |
| // Note: only send SCAN requests in response to scannable advertising |
| // events (ADV_IND, ADV_SCAN_IND). |
| if (!scannable_advertising) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "it is not scannable", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else if (!active_scanning) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "the scanner is passive", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else if (scanner_.pending_scan_request) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "an LE Scan request is already pending", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else if (!should_send_advertising_report) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "the advertising message was filtered", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else { |
| // TODO: apply privacy mode in resolving list. |
| // Scan requests with public or random device addresses must be ignored |
| // when the peer has network privacy mode. |
| |
| AddressWithType public_address{address_, |
| AddressType::PUBLIC_DEVICE_ADDRESS}; |
| AddressWithType random_address{random_address_, |
| AddressType::RANDOM_DEVICE_ADDRESS}; |
| std::optional<AddressWithType> resolvable_scanning_address = |
| GenerateResolvablePrivateAddress(resolved_advertising_address, |
| IrkSelection::Local); |
| |
| // The ScanA field of the scanning PDU is generated using the |
| // Resolving List’s Local IRK value and the Resolvable Private Address |
| // Generation procedure (see Section 1.3.2.2), or the address is provided |
| // by the Host. |
| AddressWithType scanning_address; |
| switch (scanner_.own_address_type) { |
| case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS: |
| scanning_address = public_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS: |
| // The random address is checked in Le_Set_Scan_Enable or |
| // Le_Set_Extended_Scan_Enable. |
| ASSERT(random_address_ != Address::kEmpty); |
| scanning_address = random_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS: |
| scanning_address = resolvable_scanning_address.value_or(public_address); |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS: |
| // The random address is checked in Le_Set_Scan_Enable or |
| // Le_Set_Extended_Scan_Enable. |
| ASSERT(random_address_ != Address::kEmpty); |
| scanning_address = resolvable_scanning_address.value_or(random_address); |
| break; |
| } |
| |
| // Save the original advertising type to report if the advertising |
| // is connectable in the scan response report. |
| scanner_.connectable_scan_response = connectable_advertising; |
| scanner_.pending_scan_request = advertising_address; |
| |
| LOG_INFO( |
| "Sending LE Scan request to advertising address %s(%hhx) with scanning " |
| "address %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| scanning_address.ToString().c_str(), scanning_address.GetAddressType()); |
| |
| // The advertiser’s device address (AdvA field) in the scan request PDU |
| // shall be the same as the advertiser’s device address (AdvA field) |
| // received in the advertising PDU to which the scanner is responding. |
| SendLeLinkLayerPacket(model::packets::LeScanBuilder::Create( |
| scanning_address.GetAddress(), advertising_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| scanning_address.GetAddressType()), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()))); |
| } |
| } |
| |
| void LinkLayerController::ConnectIncomingLeLegacyAdvertisingPdu( |
| model::packets::LeLegacyAdvertisingPduView& pdu) { |
| if (!initiator_.IsEnabled()) { |
| return; |
| } |
| |
| auto advertising_type = pdu.GetAdvertisingType(); |
| bool connectable_advertising = |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_IND || |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND; |
| bool directed_advertising = |
| advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND; |
| |
| // Connection. |
| // Note: only send CONNECT requests in response to connectable advertising |
| // events (ADV_IND, ADV_DIRECT_IND). |
| if (!connectable_advertising) { |
| LOG_VERB( |
| "Legacy advertising ignored by initiator because it is not " |
| "connectable"); |
| return; |
| } |
| if (initiator_.pending_connect_request) { |
| LOG_VERB( |
| "Legacy advertising ignored because an LE Connect request is already " |
| "pending"); |
| return; |
| } |
| |
| AddressWithType advertising_address{ |
| pdu.GetSourceAddress(), |
| static_cast<AddressType>(pdu.GetAdvertisingAddressType())}; |
| |
| AddressWithType target_address{ |
| pdu.GetDestinationAddress(), |
| static_cast<AddressType>(pdu.GetTargetAddressType())}; |
| |
| AddressWithType resolved_advertising_address = |
| ResolvePrivateAddress(advertising_address, IrkSelection::Peer) |
| .value_or(advertising_address); |
| |
| AddressWithType resolved_target_address = |
| ResolvePrivateAddress(target_address, IrkSelection::Peer) |
| .value_or(target_address); |
| |
| // Vol 6, Part B § 4.3.5 Initiator filter policy. |
| switch (initiator_.initiator_filter_policy) { |
| case bluetooth::hci::InitiatorFilterPolicy::USE_PEER_ADDRESS: |
| if (resolved_advertising_address != initiator_.peer_address) { |
| LOG_VERB( |
| "Legacy advertising ignored by initiator because the " |
| "advertising address %s does not match the peer address %s", |
| resolved_advertising_address.ToString().c_str(), |
| initiator_.peer_address.ToString().c_str()); |
| return; |
| } |
| break; |
| case bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST: |
| if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) { |
| LOG_VERB( |
| "Legacy advertising ignored by initiator because the " |
| "advertising address %s is not in the filter accept list", |
| resolved_advertising_address.ToString().c_str()); |
| return; |
| } |
| break; |
| } |
| |
| // When an initiator receives a directed connectable advertising event that |
| // contains a resolvable private address for the target’s address |
| // (TargetA field) and address resolution is enabled, the Link Layer shall |
| // resolve the private address using the resolving list’s Local IRK values. |
| // An initiator that has been instructed by the Host to use Resolvable Private |
| // Addresses shall not respond to directed connectable advertising events that |
| // contain Public or Static addresses for the target’s address (TargetA |
| // field). |
| if (directed_advertising) { |
| if (!IsLocalPublicOrRandomAddress(resolved_target_address)) { |
| LOG_VERB( |
| "Directed legacy advertising ignored by initiator because the " |
| "target address %s does not match the current device addresses", |
| resolved_advertising_address.ToString().c_str()); |
| return; |
| } |
| if (resolved_target_address == target_address && |
| (initiator_.own_address_type == |
| OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS || |
| initiator_.own_address_type == |
| OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS)) { |
| LOG_VERB( |
| "Directed legacy advertising ignored by initiator because the " |
| "target address %s is static or public and the initiator is " |
| "configured to use resolvable addresses", |
| resolved_advertising_address.ToString().c_str()); |
| return; |
| } |
| } |
| |
| AddressWithType public_address{address_, AddressType::PUBLIC_DEVICE_ADDRESS}; |
| AddressWithType random_address{random_address_, |
| AddressType::RANDOM_DEVICE_ADDRESS}; |
| std::optional<AddressWithType> resolvable_initiating_address = |
| GenerateResolvablePrivateAddress(resolved_advertising_address, |
| IrkSelection::Local); |
| |
| // The Link Layer shall use resolvable private addresses for the initiator’s |
| // device address (InitA field) when initiating connection establishment with |
| // an associated device that exists in the Resolving List. |
| AddressWithType initiating_address; |
| switch (initiator_.own_address_type) { |
| case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS: |
| initiating_address = public_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS: |
| // The random address is checked in Le_Create_Connection or |
| // Le_Extended_Create_Connection. |
| ASSERT(random_address_ != Address::kEmpty); |
| initiating_address = random_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS: |
| initiating_address = |
| resolvable_initiating_address.value_or(public_address); |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS: |
| // The random address is checked in Le_Create_Connection or |
| // Le_Extended_Create_Connection. |
| ASSERT(random_address_ != Address::kEmpty); |
| initiating_address = |
| resolvable_initiating_address.value_or(random_address); |
| break; |
| } |
| |
| if (!connections_.CreatePendingLeConnection( |
| advertising_address, |
| resolved_advertising_address != advertising_address |
| ? resolved_advertising_address |
| : AddressWithType{}, |
| initiating_address)) { |
| LOG_WARN("CreatePendingLeConnection failed for connection to %s", |
| advertising_address.ToString().c_str()); |
| } |
| |
| initiator_.pending_connect_request = advertising_address; |
| |
| LOG_INFO("Sending LE Connect request to %s with initiating address %s", |
| resolved_advertising_address.ToString().c_str(), |
| initiating_address.ToString().c_str()); |
| |
| // The advertiser’s device address (AdvA field) in the initiating PDU |
| // shall be the same as the advertiser’s device address (AdvA field) |
| // received in the advertising event PDU to which the initiator is |
| // responding. |
| SendLeLinkLayerPacket(model::packets::LeConnectBuilder::Create( |
| initiating_address.GetAddress(), advertising_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| initiating_address.GetAddressType()), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()), |
| // The connection is created with the highest allowed |
| // value for the connection interval and the latency. |
| initiator_.le_1m_phy.connection_interval_max, |
| initiator_.le_1m_phy.max_latency, |
| initiator_.le_1m_phy.supervision_timeout)); |
| } |
| |
| void LinkLayerController::IncomingLeLegacyAdvertisingPdu( |
| model::packets::LinkLayerPacketView incoming, uint8_t rssi) { |
| auto pdu = model::packets::LeLegacyAdvertisingPduView::Create(incoming); |
| ASSERT(pdu.IsValid()); |
| |
| ScanIncomingLeLegacyAdvertisingPdu(pdu, rssi); |
| ConnectIncomingLeLegacyAdvertisingPdu(pdu); |
| } |
| |
| // Handle legacy advertising PDUs while in the Scanning state. |
| void LinkLayerController::ScanIncomingLeExtendedAdvertisingPdu( |
| model::packets::LeExtendedAdvertisingPduView& pdu, uint8_t rssi) { |
| if (!scanner_.IsEnabled()) { |
| return; |
| } |
| if (!ExtendedAdvertising()) { |
| LOG_VERB("Extended advertising ignored because the scanner is legacy"); |
| return; |
| } |
| |
| std::vector<uint8_t> advertising_data = pdu.GetAdvertisingData(); |
| AddressWithType advertising_address{ |
| pdu.GetSourceAddress(), |
| static_cast<AddressType>(pdu.GetAdvertisingAddressType())}; |
| |
| AddressWithType target_address{ |
| pdu.GetDestinationAddress(), |
| static_cast<AddressType>(pdu.GetTargetAddressType())}; |
| |
| bool scannable_advertising = pdu.GetScannable(); |
| bool connectable_advertising = pdu.GetConnectable(); |
| bool directed_advertising = pdu.GetDirected(); |
| |
| // TODO: check originating PHY, compare against active scanning PHYs |
| // (scanner_.le_1m_phy or scanner_.le_coded_phy). |
| |
| // When a scanner receives an advertising packet that contains a resolvable |
| // private address for the advertiser’s device address (AdvA field) and |
| // address resolution is enabled, the Link Layer shall resolve the private |
| // address. The scanner’s filter policy shall then determine if the scanner |
| // responds with a scan request. |
| AddressWithType resolved_advertising_address = |
| ResolvePrivateAddress(advertising_address, IrkSelection::Peer) |
| .value_or(advertising_address); |
| |
| std::optional<AddressWithType> resolved_target_address = |
| ResolvePrivateAddress(target_address, IrkSelection::Peer); |
| |
| if (resolved_advertising_address != advertising_address) { |
| LOG_VERB("Resolved the advertising address %s(%hhx) to %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| resolved_advertising_address.ToString().c_str(), |
| resolved_advertising_address.GetAddressType()); |
| } |
| |
| // Vol 6, Part B § 4.3.3 Scanner filter policy |
| switch (scanner_.scan_filter_policy) { |
| case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL: |
| case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY: |
| break; |
| case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY: |
| case bluetooth::hci::LeScanningFilterPolicy:: |
| FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY: |
| if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) { |
| LOG_VERB( |
| "Extended advertising ignored by scanner because the advertising " |
| "address %s(%hhx) is not in the filter accept list", |
| resolved_advertising_address.ToString().c_str(), |
| resolved_advertising_address.GetAddressType()); |
| return; |
| } |
| break; |
| } |
| |
| if (directed_advertising) { |
| switch (scanner_.scan_filter_policy) { |
| // In both basic scanner filter policy modes, a directed advertising PDU |
| // shall be ignored unless either: |
| // • the TargetA field is identical to the scanner's device address, or |
| // • the TargetA field is a resolvable private address, address |
| // resolution is enabled, and the address is resolved successfully |
| case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL: |
| case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY: |
| if (!IsLocalPublicOrRandomAddress(target_address) && |
| !(target_address.IsRpa() && resolved_target_address)) { |
| LOG_VERB( |
| "Extended advertising ignored by scanner because the directed " |
| "address %s(%hhx) does not match the current device or cannot be " |
| "resolved", |
| target_address.ToString().c_str(), |
| target_address.GetAddressType()); |
| return; |
| } |
| break; |
| // These are identical to the basic modes except |
| // that a directed advertising PDU shall be ignored unless either: |
| // • the TargetA field is identical to the scanner's device address, or |
| // • the TargetA field is a resolvable private address. |
| case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY: |
| case bluetooth::hci::LeScanningFilterPolicy:: |
| FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY: |
| if (!IsLocalPublicOrRandomAddress(target_address) && |
| !target_address.IsRpa()) { |
| LOG_VERB( |
| "Extended advertising ignored by scanner because the directed " |
| "address %s(%hhx) does not match the current device or is not a " |
| "resovable private address", |
| target_address.ToString().c_str(), |
| target_address.GetAddressType()); |
| return; |
| } |
| break; |
| } |
| } |
| |
| bool should_send_advertising_report = true; |
| if (scanner_.filter_duplicates != |
| bluetooth::hci::FilterDuplicates::DISABLED) { |
| if (scanner_.IsPacketInHistory(pdu)) { |
| should_send_advertising_report = false; |
| } else { |
| scanner_.AddPacketToHistory(pdu); |
| } |
| } |
| |
| if (should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) { |
| bluetooth::hci::LeExtendedAdvertisingResponseRaw response; |
| response.connectable_ = connectable_advertising; |
| response.scannable_ = scannable_advertising; |
| response.directed_ = directed_advertising; |
| response.scan_response_ = false; |
| response.legacy_ = false; |
| response.data_status_ = bluetooth::hci::DataStatus::COMPLETE; |
| response.address_type_ = |
| static_cast<bluetooth::hci::DirectAdvertisingAddressType>( |
| resolved_advertising_address.GetAddressType()); |
| response.address_ = resolved_advertising_address.GetAddress(); |
| response.primary_phy_ = bluetooth::hci::PrimaryPhyType::LE_1M; |
| response.secondary_phy_ = bluetooth::hci::SecondaryPhyType::NO_PACKETS; |
| response.advertising_sid_ = pdu.GetSid(); |
| response.tx_power_ = pdu.GetTxPower(); |
| response.rssi_ = rssi; |
| response.periodic_advertising_interval_ = |
| pdu.GetPeriodicAdvertisingInterval(); |
| if (directed_advertising) { |
| response.direct_address_type_ = |
| bluetooth::hci::DirectAdvertisingAddressType( |
| target_address.GetAddressType()); |
| response.direct_address_ = target_address.GetAddress(); |
| } else { |
| response.direct_address_type_ = |
| bluetooth::hci::DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED; |
| response.direct_address_ = Address::kEmpty; |
| } |
| response.advertising_data_ = advertising_data; |
| |
| // Each extended advertising report can only pass 229 bytes of |
| // advertising data (255 - size of report fields). |
| // RootCanal must fragment the report as necessary. |
| const size_t max_fragment_size = 229; |
| size_t offset = 0; |
| do { |
| size_t remaining_size = advertising_data.size() - offset; |
| size_t fragment_size = std::min(max_fragment_size, remaining_size); |
| response.data_status_ = remaining_size <= max_fragment_size |
| ? bluetooth::hci::DataStatus::COMPLETE |
| : bluetooth::hci::DataStatus::CONTINUING; |
| response.advertising_data_ = |
| std::vector(advertising_data.begin() + offset, |
| advertising_data.begin() + offset + fragment_size); |
| offset += fragment_size; |
| send_event_(bluetooth::hci::LeExtendedAdvertisingReportRawBuilder::Create( |
| {response})); |
| } while (offset < advertising_data.size()); |
| } |
| |
| // Did the user enable Active scanning ? |
| bool active_scanning = |
| (scanner_.le_1m_phy.enabled && |
| scanner_.le_1m_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE) || |
| (scanner_.le_coded_phy.enabled && |
| scanner_.le_coded_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE); |
| |
| // Active scanning. |
| // Note: only send SCAN requests in response to scannable advertising |
| // events (ADV_IND, ADV_SCAN_IND). |
| if (!scannable_advertising) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "it is not scannable", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else if (!active_scanning) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "the scanner is passive", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else if (scanner_.pending_scan_request) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "an LE Scan request is already pending", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else if (!should_send_advertising_report) { |
| LOG_VERB( |
| "Not sending LE Scan request to advertising address %s(%hhx) because " |
| "the advertising message was filtered", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| } else { |
| // TODO: apply privacy mode in resolving list. |
| // Scan requests with public or random device addresses must be ignored |
| // when the peer has network privacy mode. |
| |
| AddressWithType public_address{address_, |
| AddressType::PUBLIC_DEVICE_ADDRESS}; |
| AddressWithType random_address{random_address_, |
| AddressType::RANDOM_DEVICE_ADDRESS}; |
| std::optional<AddressWithType> resolvable_address = |
| GenerateResolvablePrivateAddress(resolved_advertising_address, |
| IrkSelection::Local); |
| |
| // The ScanA field of the scanning PDU is generated using the |
| // Resolving List’s Local IRK value and the Resolvable Private Address |
| // Generation procedure (see Section 1.3.2.2), or the address is provided |
| // by the Host. |
| AddressWithType scanning_address; |
| std::optional<AddressWithType> resolvable_scanning_address; |
| switch (scanner_.own_address_type) { |
| case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS: |
| scanning_address = public_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS: |
| // The random address is checked in Le_Set_Scan_Enable or |
| // Le_Set_Extended_Scan_Enable. |
| ASSERT(random_address_ != Address::kEmpty); |
| scanning_address = random_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS: |
| scanning_address = resolvable_address.value_or(public_address); |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS: |
| // The random address is checked in Le_Set_Scan_Enable or |
| // Le_Set_Extended_Scan_Enable. |
| ASSERT(random_address_ != Address::kEmpty); |
| scanning_address = resolvable_address.value_or(random_address); |
| break; |
| } |
| |
| // Save the original advertising type to report if the advertising |
| // is connectable in the scan response report. |
| scanner_.connectable_scan_response = connectable_advertising; |
| scanner_.pending_scan_request = advertising_address; |
| |
| LOG_INFO( |
| "Sending LE Scan request to advertising address %s(%hhx) with scanning " |
| "address %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| scanning_address.ToString().c_str(), scanning_address.GetAddressType()); |
| |
| // The advertiser’s device address (AdvA field) in the scan request PDU |
| // shall be the same as the advertiser’s device address (AdvA field) |
| // received in the advertising PDU to which the scanner is responding. |
| SendLeLinkLayerPacket(model::packets::LeScanBuilder::Create( |
| scanning_address.GetAddress(), advertising_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| scanning_address.GetAddressType()), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()))); |
| } |
| } |
| |
| void LinkLayerController::ConnectIncomingLeExtendedAdvertisingPdu( |
| model::packets::LeExtendedAdvertisingPduView& pdu) { |
| if (!initiator_.IsEnabled()) { |
| return; |
| } |
| if (!ExtendedAdvertising()) { |
| LOG_VERB("Extended advertising ignored because the initiator is legacy"); |
| return; |
| } |
| |
| // Connection. |
| // Note: only send CONNECT requests in response to connectable advertising |
| // events (ADV_IND, ADV_DIRECT_IND). |
| if (!pdu.GetConnectable()) { |
| LOG_VERB( |
| "Extended advertising ignored by initiator because it is not " |
| "connectable"); |
| return; |
| } |
| if (initiator_.pending_connect_request) { |
| LOG_VERB( |
| "Extended advertising ignored because an LE Connect request is already " |
| "pending"); |
| return; |
| } |
| |
| AddressWithType advertising_address{ |
| pdu.GetSourceAddress(), |
| static_cast<AddressType>(pdu.GetAdvertisingAddressType())}; |
| |
| AddressWithType target_address{ |
| pdu.GetDestinationAddress(), |
| static_cast<AddressType>(pdu.GetTargetAddressType())}; |
| |
| AddressWithType resolved_advertising_address = |
| ResolvePrivateAddress(advertising_address, IrkSelection::Peer) |
| .value_or(advertising_address); |
| |
| AddressWithType resolved_target_address = |
| ResolvePrivateAddress(target_address, IrkSelection::Peer) |
| .value_or(target_address); |
| |
| // Vol 6, Part B § 4.3.5 Initiator filter policy. |
| switch (initiator_.initiator_filter_policy) { |
| case bluetooth::hci::InitiatorFilterPolicy::USE_PEER_ADDRESS: |
| if (resolved_advertising_address != initiator_.peer_address) { |
| LOG_VERB( |
| "Extended advertising ignored by initiator because the " |
| "advertising address %s does not match the peer address %s", |
| resolved_advertising_address.ToString().c_str(), |
| initiator_.peer_address.ToString().c_str()); |
| return; |
| } |
| break; |
| case bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST: |
| if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) { |
| LOG_VERB( |
| "Extended advertising ignored by initiator because the " |
| "advertising address %s is not in the filter accept list", |
| resolved_advertising_address.ToString().c_str()); |
| return; |
| } |
| break; |
| } |
| |
| // When an initiator receives a directed connectable advertising event that |
| // contains a resolvable private address for the target’s address |
| // (TargetA field) and address resolution is enabled, the Link Layer shall |
| // resolve the private address using the resolving list’s Local IRK values. |
| // An initiator that has been instructed by the Host to use Resolvable Private |
| // Addresses shall not respond to directed connectable advertising events that |
| // contain Public or Static addresses for the target’s address (TargetA |
| // field). |
| if (pdu.GetDirected()) { |
| if (!IsLocalPublicOrRandomAddress(resolved_target_address)) { |
| LOG_VERB( |
| "Directed extended advertising ignored by initiator because the " |
| "target address %s does not match the current device addresses", |
| resolved_advertising_address.ToString().c_str()); |
| return; |
| } |
| if (resolved_target_address == target_address && |
| (initiator_.own_address_type == |
| OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS || |
| initiator_.own_address_type == |
| OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS)) { |
| LOG_VERB( |
| "Directed extended advertising ignored by initiator because the " |
| "target address %s is static or public and the initiator is " |
| "configured to use resolvable addresses", |
| resolved_advertising_address.ToString().c_str()); |
| return; |
| } |
| } |
| |
| AddressWithType public_address{address_, AddressType::PUBLIC_DEVICE_ADDRESS}; |
| AddressWithType random_address{random_address_, |
| AddressType::RANDOM_DEVICE_ADDRESS}; |
| std::optional<AddressWithType> resolvable_initiating_address = |
| GenerateResolvablePrivateAddress(resolved_advertising_address, |
| IrkSelection::Local); |
| |
| // The Link Layer shall use resolvable private addresses for the initiator’s |
| // device address (InitA field) when initiating connection establishment with |
| // an associated device that exists in the Resolving List. |
| AddressWithType initiating_address; |
| switch (initiator_.own_address_type) { |
| case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS: |
| initiating_address = public_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS: |
| // The random address is checked in Le_Create_Connection or |
| // Le_Extended_Create_Connection. |
| ASSERT(random_address_ != Address::kEmpty); |
| initiating_address = random_address; |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS: |
| initiating_address = |
| resolvable_initiating_address.value_or(public_address); |
| break; |
| case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS: |
| // The random address is checked in Le_Create_Connection or |
| // Le_Extended_Create_Connection. |
| ASSERT(random_address_ != Address::kEmpty); |
| initiating_address = |
| resolvable_initiating_address.value_or(random_address); |
| break; |
| } |
| |
| if (!connections_.CreatePendingLeConnection( |
| advertising_address, |
| resolved_advertising_address != advertising_address |
| ? resolved_advertising_address |
| : AddressWithType{}, |
| initiating_address)) { |
| LOG_WARN("CreatePendingLeConnection failed for connection to %s", |
| advertising_address.ToString().c_str()); |
| } |
| |
| initiator_.pending_connect_request = advertising_address; |
| |
| LOG_INFO("Sending LE Connect request to %s with initiating address %s", |
| resolved_advertising_address.ToString().c_str(), |
| initiating_address.ToString().c_str()); |
| |
| // The advertiser’s device address (AdvA field) in the initiating PDU |
| // shall be the same as the advertiser’s device address (AdvA field) |
| // received in the advertising event PDU to which the initiator is |
| // responding. |
| SendLeLinkLayerPacket(model::packets::LeConnectBuilder::Create( |
| initiating_address.GetAddress(), advertising_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| initiating_address.GetAddressType()), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()), |
| // The connection is created with the highest allowed value |
| // for the connection interval and the latency. |
| initiator_.le_1m_phy.connection_interval_max, |
| initiator_.le_1m_phy.max_latency, |
| initiator_.le_1m_phy.supervision_timeout)); |
| } |
| |
| void LinkLayerController::IncomingLeExtendedAdvertisingPdu( |
| model::packets::LinkLayerPacketView incoming, uint8_t rssi) { |
| auto pdu = model::packets::LeExtendedAdvertisingPduView::Create(incoming); |
| ASSERT(pdu.IsValid()); |
| |
| ScanIncomingLeExtendedAdvertisingPdu(pdu, rssi); |
| ConnectIncomingLeExtendedAdvertisingPdu(pdu); |
| } |
| |
| void LinkLayerController::IncomingLePeriodicAdvertisingPdu( |
| model::packets::LinkLayerPacketView incoming, uint8_t rssi) { |
| auto pdu = model::packets::LePeriodicAdvertisingPduView::Create(incoming); |
| ASSERT(pdu.IsValid()); |
| |
| // Synchronization with periodic advertising only occurs while extended |
| // scanning is enabled. |
| if (!scanner_.IsEnabled()) { |
| return; |
| } |
| if (!ExtendedAdvertising()) { |
| LOG_VERB("Extended advertising ignored because the scanner is legacy"); |
| return; |
| } |
| |
| AddressWithType advertiser_address{ |
| pdu.GetSourceAddress(), |
| static_cast<AddressType>(pdu.GetAdvertisingAddressType())}; |
| uint8_t advertising_sid = pdu.GetSid(); |
| |
| // When a scanner receives an advertising packet that contains a resolvable |
| // private address for the advertiser's device address (AdvA field) and |
| // address resolution is enabled, the Link Layer shall resolve the private |
| // address. The scanner's periodic sync establishment filter policy shall |
| // determine if the scanner processes the advertising packet. |
| AddressWithType resolved_advertiser_address = |
| ResolvePrivateAddress(advertiser_address, IrkSelection::Peer) |
| .value_or(advertiser_address); |
| |
| bluetooth::hci::AdvertiserAddressType advertiser_address_type; |
| switch (resolved_advertiser_address.GetAddressType()) { |
| case AddressType::PUBLIC_DEVICE_ADDRESS: |
| case AddressType::PUBLIC_IDENTITY_ADDRESS: |
| default: |
| advertiser_address_type = bluetooth::hci::AdvertiserAddressType:: |
| PUBLIC_DEVICE_OR_IDENTITY_ADDRESS; |
| break; |
| case AddressType::RANDOM_DEVICE_ADDRESS: |
| case AddressType::RANDOM_IDENTITY_ADDRESS: |
| advertiser_address_type = bluetooth::hci::AdvertiserAddressType:: |
| RANDOM_DEVICE_OR_IDENTITY_ADDRESS; |
| break; |
| } |
| |
| // Check if the periodic advertising PDU matches a pending |
| // LE Periodic Advertising Create Sync command. |
| // The direct parameters or the periodic advertiser list are used |
| // depending on the synchronizing options. |
| bool matches_synchronizing = false; |
| if (synchronizing_.has_value()) { |
| matches_synchronizing = |
| synchronizing_->options.use_periodic_advertiser_list_ |
| ? LePeriodicAdvertiserListContainsDevice( |
| advertiser_address_type, |
| resolved_advertiser_address.GetAddress(), advertising_sid) |
| : synchronizing_->advertiser_address_type == |
| advertiser_address_type && |
| synchronizing_->advertiser_address == |
| resolved_advertiser_address.GetAddress() && |
| synchronizing_->advertising_sid == advertising_sid; |
| } |
| |
| // If the periodic advertising event matches the synchronizing state, |
| // create the synchronized train and report to the Host. |
| if (matches_synchronizing) { |
| LOG_INFO("Established Sync with advertiser %s[%s] - SID 0x%x", |
| advertiser_address.ToString().c_str(), |
| bluetooth::hci::AdvertiserAddressTypeText(advertiser_address_type) |
| .c_str(), |
| advertising_sid); |
| // Use the first unused Sync_Handle. |
| // Note: sync handles are allocated from a different number space |
| // compared to connection handles. |
| uint16_t sync_handle = 0; |
| for (; synchronized_.count(sync_handle) != 0; sync_handle++) { |
| } |
| |
| // Notify of the new Synchronized train. |
| if (IsLeEventUnmasked( |
| SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED)) { |
| send_event_( |
| bluetooth::hci::LePeriodicAdvertisingSyncEstablishedBuilder::Create( |
| ErrorCode::SUCCESS, sync_handle, advertising_sid, |
| resolved_advertiser_address.GetAddressType(), |
| resolved_advertiser_address.GetAddress(), |
| bluetooth::hci::SecondaryPhyType::LE_1M, |
| pdu.GetAdvertisingInterval(), |
| bluetooth::hci::ClockAccuracy::PPM_500)); |
| } |
| |
| // Update the synchronization state. |
| synchronized_.insert( |
| {sync_handle, |
| Synchronized{ |
| .advertiser_address_type = advertiser_address_type, |
| .advertiser_address = resolved_advertiser_address.GetAddress(), |
| .advertising_sid = advertising_sid, |
| .sync_handle = sync_handle, |
| .sync_timeout = synchronizing_->sync_timeout, |
| .timeout = std::chrono::steady_clock::now() + |
| synchronizing_->sync_timeout, |
| }}); |
| |
| // Quit synchronizing state. |
| synchronizing_ = {}; |
| |
| // Create Sync ensure that they are no other established syncs that |
| // already match the advertiser address and advertising SID; |
| // no need to check again. |
| return; |
| } |
| |
| // Check if the periodic advertising PDU matches any of the established |
| // syncs. |
| for (auto& [_, sync] : synchronized_) { |
| if (sync.advertiser_address_type != advertiser_address_type || |
| sync.advertiser_address != resolved_advertiser_address.GetAddress() || |
| sync.advertising_sid != advertising_sid) { |
| continue; |
| } |
| |
| // Send a Periodic Advertising event for the matching Sync, |
| // and refresh the timeout for sync termination. The periodic |
| // advertising event might need to be fragmented to fit the maximum |
| // size of an HCI event. |
| if (IsLeEventUnmasked(SubeventCode::PERIODIC_ADVERTISING_REPORT)) { |
| // Each extended advertising report can only pass 229 bytes of |
| // advertising data (255 - 8 = size of report fields). |
| std::vector<uint8_t> advertising_data = pdu.GetAdvertisingData(); |
| const size_t max_fragment_size = 247; |
| size_t offset = 0; |
| do { |
| size_t remaining_size = advertising_data.size() - offset; |
| size_t fragment_size = std::min(max_fragment_size, remaining_size); |
| |
| bluetooth::hci::DataStatus data_status = |
| remaining_size <= max_fragment_size |
| ? bluetooth::hci::DataStatus::COMPLETE |
| : bluetooth::hci::DataStatus::CONTINUING; |
| std::vector<uint8_t> fragment_data( |
| advertising_data.begin() + offset, |
| advertising_data.begin() + offset + fragment_size); |
| offset += fragment_size; |
| send_event_(bluetooth::hci::LePeriodicAdvertisingReportBuilder::Create( |
| sync.sync_handle, pdu.GetTxPower(), rssi, |
| bluetooth::hci::CteType::NO_CONSTANT_TONE_EXTENSION, data_status, |
| fragment_data)); |
| } while (offset < advertising_data.size()); |
| } |
| |
| // Refresh the timeout for the sync disconnection. |
| sync.timeout = std::chrono::steady_clock::now() + sync.sync_timeout; |
| } |
| } |
| |
| void LinkLayerController::IncomingScoConnectionRequest( |
| model::packets::LinkLayerPacketView incoming) { |
| Address address = incoming.GetSourceAddress(); |
| auto request = model::packets::ScoConnectionRequestView::Create(incoming); |
| ASSERT(request.IsValid()); |
| |
| LOG_INFO("Received eSCO connection request from %s", |
| address.ToString().c_str()); |
| |
| // Automatically reject if connection request was already sent |
| // from the current device. |
| if (connections_.HasPendingScoConnection(address)) { |
| LOG_INFO( |
| "Rejecting eSCO connection request from %s, " |
| "an eSCO connection already exist with this device", |
| address.ToString().c_str()); |
| |
| SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create( |
| GetAddress(), address, |
| (uint8_t)ErrorCode::SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED, 0, 0, 0, 0, |
| 0, 0)); |
| return; |
| } |
| |
| // Create local connection context. |
| ScoConnectionParameters connection_parameters = { |
| request.GetTransmitBandwidth(), request.GetReceiveBandwidth(), |
| request.GetMaxLatency(), request.GetVoiceSetting(), |
| request.GetRetransmissionEffort(), request.GetPacketType()}; |
| |
| bool extended = connection_parameters.IsExtended(); |
| connections_.CreateScoConnection( |
| address, connection_parameters, |
| extended ? ScoState::SCO_STATE_SENT_ESCO_CONNECTION_REQUEST |
| : ScoState::SCO_STATE_SENT_SCO_CONNECTION_REQUEST, |
| ScoDatapath::NORMAL); |
| |
| // Send connection request event and wait for Accept or Reject command. |
| send_event_(bluetooth::hci::ConnectionRequestBuilder::Create( |
| address, request.GetClassOfDevice(), |
| extended ? bluetooth::hci::ConnectionRequestLinkType::ESCO |
| : bluetooth::hci::ConnectionRequestLinkType::SCO)); |
| } |
| |
| void LinkLayerController::IncomingScoConnectionResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| Address address = incoming.GetSourceAddress(); |
| auto response = model::packets::ScoConnectionResponseView::Create(incoming); |
| ASSERT(response.IsValid()); |
| auto status = ErrorCode(response.GetStatus()); |
| bool is_legacy = connections_.IsLegacyScoConnection(address); |
| |
| LOG_INFO("Received eSCO connection response with status 0x%02x from %s", |
| static_cast<unsigned>(status), |
| incoming.GetSourceAddress().ToString().c_str()); |
| |
| if (status == ErrorCode::SUCCESS) { |
| bool extended = response.GetExtended(); |
| ScoLinkParameters link_parameters = { |
| response.GetTransmissionInterval(), |
| response.GetRetransmissionWindow(), |
| response.GetRxPacketLength(), |
| response.GetTxPacketLength(), |
| response.GetAirMode(), |
| extended, |
| }; |
| |
| connections_.AcceptPendingScoConnection( |
| address, link_parameters, [this, address] { |
| return LinkLayerController::StartScoStream(address); |
| }); |
| |
| if (is_legacy) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connections_.GetScoHandle(address), address, |
| bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED)); |
| } else { |
| send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connections_.GetScoHandle(address), address, |
| extended ? bluetooth::hci::ScoLinkType::ESCO |
| : bluetooth::hci::ScoLinkType::SCO, |
| extended ? response.GetTransmissionInterval() : 0, |
| extended ? response.GetRetransmissionWindow() : 0, |
| extended ? response.GetRxPacketLength() : 0, |
| extended ? response.GetTxPacketLength() : 0, |
| bluetooth::hci::ScoAirMode(response.GetAirMode()))); |
| } |
| } else { |
| connections_.CancelPendingScoConnection(address); |
| if (is_legacy) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| status, 0, address, bluetooth::hci::LinkType::SCO, |
| bluetooth::hci::Enable::DISABLED)); |
| } else { |
| ScoConnectionParameters connection_parameters = |
| connections_.GetScoConnectionParameters(address); |
| send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create( |
| status, 0, address, |
| connection_parameters.IsExtended() ? bluetooth::hci::ScoLinkType::ESCO |
| : bluetooth::hci::ScoLinkType::SCO, |
| 0, 0, 0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT)); |
| } |
| } |
| } |
| |
| void LinkLayerController::IncomingScoDisconnect( |
| model::packets::LinkLayerPacketView incoming) { |
| Address address = incoming.GetSourceAddress(); |
| auto request = model::packets::ScoDisconnectView::Create(incoming); |
| ASSERT(request.IsValid()); |
| auto reason = request.GetReason(); |
| uint16_t handle = connections_.GetScoHandle(address); |
| |
| LOG_INFO( |
| "Received eSCO disconnection request with" |
| " reason 0x%02x from %s", |
| static_cast<unsigned>(reason), |
| incoming.GetSourceAddress().ToString().c_str()); |
| |
| if (handle != kReservedHandle) { |
| connections_.Disconnect( |
| handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); |
| SendDisconnectionCompleteEvent(handle, ErrorCode(reason)); |
| } |
| } |
| |
| void LinkLayerController::IncomingLmpPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| Address address = incoming.GetSourceAddress(); |
| auto request = model::packets::LmpView::Create(incoming); |
| ASSERT(request.IsValid()); |
| auto payload = request.GetPayload(); |
| auto packet = std::vector(payload.begin(), payload.end()); |
| |
| ASSERT(link_manager_ingest_lmp( |
| lm_.get(), reinterpret_cast<uint8_t(*)[6]>(address.data()), packet.data(), |
| packet.size())); |
| } |
| |
| uint16_t LinkLayerController::HandleLeConnection( |
| AddressWithType address, AddressWithType own_address, |
| bluetooth::hci::Role role, uint16_t connection_interval, |
| uint16_t connection_latency, uint16_t supervision_timeout, |
| bool send_le_channel_selection_algorithm_event) { |
| // Note: the HCI_LE_Connection_Complete event is not sent if the |
| // HCI_LE_Enhanced_Connection_Complete event (see Section 7.7.65.10) is |
| // unmasked. |
| |
| uint16_t handle = connections_.CreateLeConnection(address, own_address, role); |
| if (handle == kReservedHandle) { |
| LOG_WARN("No pending connection for connection from %s", |
| address.ToString().c_str()); |
| return kReservedHandle; |
| } |
| |
| if (IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) { |
| AddressWithType peer_resolved_address = |
| connections_.GetResolvedAddress(handle); |
| Address peer_resolvable_private_address; |
| Address connection_address = address.GetAddress(); |
| AddressType peer_address_type = address.GetAddressType(); |
| if (peer_resolved_address != AddressWithType()) { |
| peer_resolvable_private_address = address.GetAddress(); |
| if (peer_resolved_address.GetAddressType() == |
| AddressType::PUBLIC_DEVICE_ADDRESS) { |
| peer_address_type = AddressType::PUBLIC_IDENTITY_ADDRESS; |
| } else if (peer_resolved_address.GetAddressType() == |
| AddressType::RANDOM_DEVICE_ADDRESS) { |
| peer_address_type = AddressType::RANDOM_IDENTITY_ADDRESS; |
| } else { |
| LOG_WARN("Unhandled resolved address type %s -> %s", |
| address.ToString().c_str(), |
| peer_resolved_address.ToString().c_str()); |
| } |
| connection_address = peer_resolved_address.GetAddress(); |
| } |
| Address local_resolved_address = own_address.GetAddress(); |
| if (local_resolved_address == GetAddress() || |
| local_resolved_address == random_address_) { |
| local_resolved_address = Address::kEmpty; |
| } |
| |
| send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, role, peer_address_type, connection_address, |
| local_resolved_address, peer_resolvable_private_address, |
| connection_interval, connection_latency, supervision_timeout, |
| static_cast<bluetooth::hci::ClockAccuracy>(0x00))); |
| } else if (IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, role, address.GetAddressType(), |
| address.GetAddress(), connection_interval, connection_latency, |
| supervision_timeout, static_cast<bluetooth::hci::ClockAccuracy>(0x00))); |
| } |
| |
| // Note: the HCI_LE_Connection_Complete event is immediately followed by |
| // an HCI_LE_Channel_Selection_Algorithm event if the connection is created |
| // using the LE_Extended_Create_Connection command (see Section 7.7.8.66). |
| if (send_le_channel_selection_algorithm_event && |
| IsLeEventUnmasked(SubeventCode::CHANNEL_SELECTION_ALGORITHM)) { |
| // The selection channel algorithm probably will have no impact |
| // on emulation. |
| send_event_(bluetooth::hci::LeChannelSelectionAlgorithmBuilder::Create( |
| handle, bluetooth::hci::ChannelSelectionAlgorithm::ALGORITHM_1)); |
| } |
| |
| if (own_address.GetAddress() == initiator_.initiating_address) { |
| initiator_.initiating_address = Address::kEmpty; |
| } |
| return handle; |
| } |
| |
| // Handle CONNECT_IND PDUs for the legacy advertiser. |
| bool LinkLayerController::ProcessIncomingLegacyConnectRequest( |
| model::packets::LeConnectView const& connect_ind) { |
| if (!legacy_advertiser_.IsEnabled()) { |
| return false; |
| } |
| if (!legacy_advertiser_.IsConnectable()) { |
| LOG_VERB( |
| "LE Connect request ignored by legacy advertiser because it is not " |
| "connectable"); |
| return false; |
| } |
| |
| AddressWithType advertising_address{ |
| connect_ind.GetDestinationAddress(), |
| static_cast<AddressType>(connect_ind.GetAdvertisingAddressType()), |
| }; |
| |
| AddressWithType initiating_address{ |
| connect_ind.GetSourceAddress(), |
| static_cast<AddressType>(connect_ind.GetInitiatingAddressType()), |
| }; |
| |
| if (legacy_advertiser_.GetAdvertisingAddress() != advertising_address) { |
| LOG_VERB( |
| "LE Connect request ignored by legacy advertiser because the " |
| "advertising address %s(%hhx) does not match %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| legacy_advertiser_.GetAdvertisingAddress().ToString().c_str(), |
| legacy_advertiser_.GetAdvertisingAddress().GetAddressType()); |
| return false; |
| } |
| |
| // When an advertiser receives a connection request that contains a resolvable |
| // private address for the initiator’s address (InitA field) and address |
| // resolution is enabled, the Link Layer shall resolve the private address. |
| // The advertising filter policy shall then determine if the |
| // advertiser establishes a connection. |
| AddressWithType resolved_initiating_address = |
| ResolvePrivateAddress(initiating_address, IrkSelection::Peer) |
| .value_or(initiating_address); |
| |
| if (resolved_initiating_address != initiating_address) { |
| LOG_VERB("Resolved the initiating address %s(%hhx) to %s(%hhx)", |
| initiating_address.ToString().c_str(), |
| initiating_address.GetAddressType(), |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType()); |
| } |
| |
| // When the Link Layer is [...] connectable directed advertising events the |
| // advertising filter policy shall be ignored. |
| if (legacy_advertiser_.IsDirected()) { |
| if (legacy_advertiser_.GetTargetAddress() != resolved_initiating_address) { |
| LOG_VERB( |
| "LE Connect request ignored by legacy advertiser because the " |
| "initiating address %s(%hhx) does not match the target address " |
| "%s(%hhx)", |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType(), |
| legacy_advertiser_.GetTargetAddress().ToString().c_str(), |
| legacy_advertiser_.GetTargetAddress().GetAddressType()); |
| return false; |
| } |
| } else { |
| // Check if initiator address is in the filter accept list |
| // for this advertiser. |
| switch (legacy_advertiser_.advertising_filter_policy) { |
| case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN: |
| break; |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT: |
| if (!LeFilterAcceptListContainsDevice(resolved_initiating_address)) { |
| LOG_VERB( |
| "LE Connect request ignored by legacy advertiser because the " |
| "initiating address %s(%hhx) is not in the filter accept list", |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType()); |
| return false; |
| } |
| break; |
| } |
| } |
| |
| LOG_INFO( |
| "Accepting LE Connect request to legacy advertiser from initiating " |
| "address %s(%hhx)", |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType()); |
| |
| if (!connections_.CreatePendingLeConnection( |
| initiating_address, |
| resolved_initiating_address != initiating_address |
| ? resolved_initiating_address |
| : AddressWithType{}, |
| advertising_address)) { |
| LOG_WARN( |
| "CreatePendingLeConnection failed for connection from %s (type %hhx)", |
| initiating_address.GetAddress().ToString().c_str(), |
| initiating_address.GetAddressType()); |
| return false; |
| } |
| |
| (void)HandleLeConnection( |
| initiating_address, advertising_address, bluetooth::hci::Role::PERIPHERAL, |
| connect_ind.GetConnInterval(), connect_ind.GetConnPeripheralLatency(), |
| connect_ind.GetConnSupervisionTimeout(), false); |
| |
| SendLeLinkLayerPacket(model::packets::LeConnectCompleteBuilder::Create( |
| advertising_address.GetAddress(), initiating_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| initiating_address.GetAddressType()), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()), |
| connect_ind.GetConnInterval(), connect_ind.GetConnPeripheralLatency(), |
| connect_ind.GetConnSupervisionTimeout())); |
| |
| legacy_advertiser_.Disable(); |
| return true; |
| } |
| |
| // Handle CONNECT_IND PDUs for the selected extended advertiser. |
| bool LinkLayerController::ProcessIncomingExtendedConnectRequest( |
| ExtendedAdvertiser& advertiser, |
| model::packets::LeConnectView const& connect_ind) { |
| if (!advertiser.IsEnabled()) { |
| return false; |
| } |
| if (!advertiser.IsConnectable()) { |
| LOG_VERB( |
| "LE Connect request ignored by extended advertiser %d because it is " |
| "not connectable", |
| advertiser.advertising_handle); |
| return false; |
| } |
| |
| AddressWithType advertising_address{ |
| connect_ind.GetDestinationAddress(), |
| static_cast<AddressType>(connect_ind.GetAdvertisingAddressType()), |
| }; |
| |
| AddressWithType initiating_address{ |
| connect_ind.GetSourceAddress(), |
| static_cast<AddressType>(connect_ind.GetInitiatingAddressType()), |
| }; |
| |
| if (advertiser.GetAdvertisingAddress() != advertising_address) { |
| LOG_VERB( |
| "LE Connect request ignored by extended advertiser %d because the " |
| "advertising address %s(%hhx) does not match %s(%hhx)", |
| advertiser.advertising_handle, advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| advertiser.GetAdvertisingAddress().ToString().c_str(), |
| advertiser.GetAdvertisingAddress().GetAddressType()); |
| return false; |
| } |
| |
| // When an advertiser receives a connection request that contains a resolvable |
| // private address for the initiator’s address (InitA field) and address |
| // resolution is enabled, the Link Layer shall resolve the private address. |
| // The advertising filter policy shall then determine if the |
| // advertiser establishes a connection. |
| AddressWithType resolved_initiating_address = |
| ResolvePrivateAddress(initiating_address, IrkSelection::Peer) |
| .value_or(initiating_address); |
| |
| if (resolved_initiating_address != initiating_address) { |
| LOG_VERB("Resolved the initiating address %s(%hhx) to %s(%hhx)", |
| initiating_address.ToString().c_str(), |
| initiating_address.GetAddressType(), |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType()); |
| } |
| |
| // When the Link Layer is [...] connectable directed advertising events the |
| // advertising filter policy shall be ignored. |
| if (advertiser.IsDirected()) { |
| if (advertiser.GetTargetAddress() != resolved_initiating_address) { |
| LOG_VERB( |
| "LE Connect request ignored by extended advertiser %d because the " |
| "initiating address %s(%hhx) does not match the target address " |
| "%s(%hhx)", |
| advertiser.advertising_handle, |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType(), |
| advertiser.GetTargetAddress().ToString().c_str(), |
| advertiser.GetTargetAddress().GetAddressType()); |
| return false; |
| } |
| } else { |
| // Check if initiator address is in the filter accept list |
| // for this advertiser. |
| switch (advertiser.advertising_filter_policy) { |
| case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN: |
| break; |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT: |
| if (!LeFilterAcceptListContainsDevice(resolved_initiating_address)) { |
| LOG_VERB( |
| "LE Connect request ignored by extended advertiser %d because " |
| "the initiating address %s(%hhx) is not in the filter accept " |
| "list", |
| advertiser.advertising_handle, |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType()); |
| return false; |
| } |
| break; |
| } |
| } |
| |
| LOG_INFO( |
| "Accepting LE Connect request to extended advertiser %d from initiating " |
| "address %s(%hhx)", |
| advertiser.advertising_handle, |
| resolved_initiating_address.ToString().c_str(), |
| resolved_initiating_address.GetAddressType()); |
| |
| if (!connections_.CreatePendingLeConnection( |
| initiating_address, |
| resolved_initiating_address != initiating_address |
| ? resolved_initiating_address |
| : AddressWithType{}, |
| advertising_address)) { |
| LOG_WARN( |
| "CreatePendingLeConnection failed for connection from %s (type %hhx)", |
| initiating_address.GetAddress().ToString().c_str(), |
| initiating_address.GetAddressType()); |
| return false; |
| } |
| |
| advertiser.Disable(); |
| |
| uint16_t connection_handle = HandleLeConnection( |
| initiating_address, advertising_address, bluetooth::hci::Role::PERIPHERAL, |
| connect_ind.GetConnInterval(), connect_ind.GetConnPeripheralLatency(), |
| connect_ind.GetConnSupervisionTimeout(), false); |
| |
| SendLeLinkLayerPacket(model::packets::LeConnectCompleteBuilder::Create( |
| advertising_address.GetAddress(), initiating_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| initiating_address.GetAddressType()), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()), |
| connect_ind.GetConnInterval(), connect_ind.GetConnPeripheralLatency(), |
| connect_ind.GetConnSupervisionTimeout())); |
| |
| // If the advertising set is connectable and a connection gets created, an |
| // HCI_LE_Connection_Complete or HCI_LE_Enhanced_Connection_Complete |
| // event shall be generated followed by an HCI_LE_Advertising_Set_Terminated |
| // event with the Status parameter set to 0x00. The Controller should not send |
| // any other events in between these two events |
| |
| if (IsLeEventUnmasked(SubeventCode::ADVERTISING_SET_TERMINATED)) { |
| send_event_(bluetooth::hci::LeAdvertisingSetTerminatedBuilder::Create( |
| ErrorCode::SUCCESS, advertiser.advertising_handle, connection_handle, |
| advertiser.num_completed_extended_advertising_events)); |
| } |
| |
| return true; |
| } |
| |
| void LinkLayerController::IncomingLeConnectPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| model::packets::LeConnectView connect = |
| model::packets::LeConnectView::Create(incoming); |
| ASSERT(connect.IsValid()); |
| |
| if (ProcessIncomingLegacyConnectRequest(connect)) { |
| return; |
| } |
| |
| for (auto& [_, advertiser] : extended_advertisers_) { |
| if (ProcessIncomingExtendedConnectRequest(advertiser, connect)) { |
| return; |
| } |
| } |
| } |
| |
| void LinkLayerController::IncomingLeConnectCompletePacket( |
| model::packets::LinkLayerPacketView incoming) { |
| auto complete = model::packets::LeConnectCompleteView::Create(incoming); |
| ASSERT(complete.IsValid()); |
| |
| AddressWithType advertising_address{ |
| incoming.GetSourceAddress(), static_cast<bluetooth::hci::AddressType>( |
| complete.GetAdvertisingAddressType())}; |
| |
| LOG_INFO( |
| "Received LE Connect complete response with advertising address %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType()); |
| |
| HandleLeConnection(advertising_address, |
| AddressWithType(incoming.GetDestinationAddress(), |
| static_cast<bluetooth::hci::AddressType>( |
| complete.GetInitiatingAddressType())), |
| bluetooth::hci::Role::CENTRAL, complete.GetConnInterval(), |
| complete.GetConnPeripheralLatency(), |
| complete.GetConnSupervisionTimeout(), |
| ExtendedAdvertising()); |
| |
| initiator_.pending_connect_request = {}; |
| initiator_.Disable(); |
| } |
| |
| void LinkLayerController::IncomingLeConnectionParameterRequest( |
| model::packets::LinkLayerPacketView incoming) { |
| auto request = |
| model::packets::LeConnectionParameterRequestView::Create(incoming); |
| ASSERT(request.IsValid()); |
| Address peer = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(peer); |
| if (handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| peer.ToString().c_str()); |
| return; |
| } |
| |
| if (IsLeEventUnmasked(SubeventCode::REMOTE_CONNECTION_PARAMETER_REQUEST)) { |
| send_event_( |
| bluetooth::hci::LeRemoteConnectionParameterRequestBuilder::Create( |
| handle, request.GetIntervalMin(), request.GetIntervalMax(), |
| request.GetLatency(), request.GetTimeout())); |
| } else { |
| // If the request is being indicated to the Host and the event to the Host |
| // is masked, then the Link Layer shall issue an LL_REJECT_EXT_IND PDU with |
| // the ErrorCode set to Unsupported Remote Feature (0x1A). |
| SendLeLinkLayerPacket( |
| model::packets::LeConnectionParameterUpdateBuilder::Create( |
| request.GetDestinationAddress(), request.GetSourceAddress(), |
| static_cast<uint8_t>(ErrorCode::UNSUPPORTED_REMOTE_OR_LMP_FEATURE), |
| 0, 0, 0)); |
| } |
| } |
| |
| void LinkLayerController::IncomingLeConnectionParameterUpdate( |
| model::packets::LinkLayerPacketView incoming) { |
| auto update = |
| model::packets::LeConnectionParameterUpdateView::Create(incoming); |
| ASSERT(update.IsValid()); |
| Address peer = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(peer); |
| if (handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| peer.ToString().c_str()); |
| return; |
| } |
| if (IsLeEventUnmasked(SubeventCode::CONNECTION_UPDATE_COMPLETE)) { |
| send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create( |
| static_cast<ErrorCode>(update.GetStatus()), handle, |
| update.GetInterval(), update.GetLatency(), update.GetTimeout())); |
| } |
| } |
| |
| void LinkLayerController::IncomingLeEncryptConnection( |
| model::packets::LinkLayerPacketView incoming) { |
| LOG_INFO("IncomingLeEncryptConnection"); |
| |
| Address peer = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(peer); |
| if (handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| peer.ToString().c_str()); |
| return; |
| } |
| auto le_encrypt = model::packets::LeEncryptConnectionView::Create(incoming); |
| ASSERT(le_encrypt.IsValid()); |
| |
| // TODO: Save keys to check |
| |
| if (IsEventUnmasked(EventCode::LE_META_EVENT)) { |
| send_event_(bluetooth::hci::LeLongTermKeyRequestBuilder::Create( |
| handle, le_encrypt.GetRand(), le_encrypt.GetEdiv())); |
| } |
| } |
| |
| void LinkLayerController::IncomingLeEncryptConnectionResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| LOG_INFO("IncomingLeEncryptConnectionResponse"); |
| // TODO: Check keys |
| uint16_t handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| if (handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| incoming.GetSourceAddress().ToString().c_str()); |
| return; |
| } |
| ErrorCode status = ErrorCode::SUCCESS; |
| auto response = |
| model::packets::LeEncryptConnectionResponseView::Create(incoming); |
| ASSERT(response.IsValid()); |
| |
| bool success = true; |
| // Zero LTK is a rejection |
| if (response.GetLtk() == std::array<uint8_t, 16>{0}) { |
| status = ErrorCode::AUTHENTICATION_FAILURE; |
| success = false; |
| } |
| |
| if (connections_.IsEncrypted(handle)) { |
| if (IsEventUnmasked(EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE)) { |
| send_event_(bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create( |
| status, handle)); |
| } |
| } else if (success) { |
| connections_.Encrypt(handle); |
| if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) { |
| send_event_(bluetooth::hci::EncryptionChangeBuilder::Create( |
| status, handle, bluetooth::hci::EncryptionEnabled::ON)); |
| } |
| } else { |
| if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) { |
| send_event_(bluetooth::hci::EncryptionChangeBuilder::Create( |
| status, handle, bluetooth::hci::EncryptionEnabled::OFF)); |
| } |
| } |
| } |
| |
| void LinkLayerController::IncomingLeReadRemoteFeatures( |
| model::packets::LinkLayerPacketView incoming) { |
| uint16_t handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| ErrorCode status = ErrorCode::SUCCESS; |
| if (handle == kReservedHandle) { |
| LOG_WARN("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| incoming.GetSourceAddress().ToString().c_str()); |
| } |
| SendLeLinkLayerPacket( |
| model::packets::LeReadRemoteFeaturesResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress(), |
| GetLeSupportedFeatures(), static_cast<uint8_t>(status))); |
| } |
| |
| void LinkLayerController::IncomingLeReadRemoteFeaturesResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| uint16_t handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| ErrorCode status = ErrorCode::SUCCESS; |
| auto response = |
| model::packets::LeReadRemoteFeaturesResponseView::Create(incoming); |
| ASSERT(response.IsValid()); |
| if (handle == kReservedHandle) { |
| LOG_INFO("@%s: Unknown connection @%s", |
| incoming.GetDestinationAddress().ToString().c_str(), |
| incoming.GetSourceAddress().ToString().c_str()); |
| status = ErrorCode::UNKNOWN_CONNECTION; |
| } else { |
| status = static_cast<ErrorCode>(response.GetStatus()); |
| } |
| if (IsEventUnmasked(EventCode::LE_META_EVENT)) { |
| send_event_(bluetooth::hci::LeReadRemoteFeaturesCompleteBuilder::Create( |
| status, handle, response.GetFeatures())); |
| } |
| } |
| |
| void LinkLayerController::ProcessIncomingLegacyScanRequest( |
| AddressWithType scanning_address, AddressWithType resolved_scanning_address, |
| AddressWithType advertising_address) { |
| // Check if the advertising addresses matches the legacy |
| // advertising address. |
| if (!legacy_advertiser_.IsEnabled()) { |
| return; |
| } |
| if (!legacy_advertiser_.IsScannable()) { |
| LOG_VERB( |
| "LE Scan request ignored by legacy advertiser because it is not " |
| "scannable"); |
| return; |
| } |
| |
| if (advertising_address != legacy_advertiser_.advertising_address) { |
| LOG_VERB( |
| "LE Scan request ignored by legacy advertiser because the advertising " |
| "address %s(%hhx) does not match %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| legacy_advertiser_.GetAdvertisingAddress().ToString().c_str(), |
| legacy_advertiser_.GetAdvertisingAddress().GetAddressType()); |
| return; |
| } |
| |
| // Check if scanner address is in the filter accept list |
| // for this advertiser. |
| switch (legacy_advertiser_.advertising_filter_policy) { |
| case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT: |
| break; |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT: |
| if (!LeFilterAcceptListContainsDevice(resolved_scanning_address)) { |
| LOG_VERB( |
| "LE Scan request ignored by legacy advertiser because the scanning " |
| "address %s(%hhx) is not in the filter accept list", |
| resolved_scanning_address.ToString().c_str(), |
| resolved_scanning_address.GetAddressType()); |
| return; |
| } |
| break; |
| } |
| |
| LOG_INFO( |
| "Accepting LE Scan request to legacy advertiser from scanning address " |
| "%s(%hhx)", |
| resolved_scanning_address.ToString().c_str(), |
| resolved_scanning_address.GetAddressType()); |
| |
| // Generate the SCAN_RSP packet. |
| // Note: If the advertiser processes the scan request, the advertiser’s |
| // device address (AdvA field) in the SCAN_RSP PDU shall be the same as |
| // the advertiser’s device address (AdvA field) in the SCAN_REQ PDU to |
| // which it is responding. |
| SendLeLinkLayerPacket( |
| model::packets::LeScanResponseBuilder::Create( |
| advertising_address.GetAddress(), scanning_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()), |
| legacy_advertiser_.scan_response_data), |
| properties_.le_advertising_physical_channel_tx_power); |
| } |
| |
| void LinkLayerController::ProcessIncomingExtendedScanRequest( |
| ExtendedAdvertiser const& advertiser, AddressWithType scanning_address, |
| AddressWithType resolved_scanning_address, |
| AddressWithType advertising_address) { |
| // Check if the advertising addresses matches the legacy |
| // advertising address. |
| if (!advertiser.IsEnabled()) { |
| return; |
| } |
| if (!advertiser.IsScannable()) { |
| LOG_VERB( |
| "LE Scan request ignored by extended advertiser %d because it is not " |
| "scannable", |
| advertiser.advertising_handle); |
| return; |
| } |
| |
| if (advertising_address != advertiser.advertising_address) { |
| LOG_VERB( |
| "LE Scan request ignored by extended advertiser %d because the " |
| "advertising address %s(%hhx) does not match %s(%hhx)", |
| advertiser.advertising_handle, advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| advertiser.GetAdvertisingAddress().ToString().c_str(), |
| advertiser.GetAdvertisingAddress().GetAddressType()); |
| return; |
| } |
| |
| // Check if scanner address is in the filter accept list |
| // for this advertiser. |
| switch (advertiser.advertising_filter_policy) { |
| case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT: |
| break; |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN: |
| case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT: |
| if (!LeFilterAcceptListContainsDevice(resolved_scanning_address)) { |
| LOG_VERB( |
| "LE Scan request ignored by extended advertiser %d because the " |
| "scanning address %s(%hhx) is not in the filter accept list", |
| advertiser.advertising_handle, |
| resolved_scanning_address.ToString().c_str(), |
| resolved_scanning_address.GetAddressType()); |
| return; |
| } |
| break; |
| } |
| |
| // Check if the scanner address is the target address in the case of |
| // scannable directed event types. |
| if (advertiser.IsDirected() && |
| advertiser.target_address != resolved_scanning_address) { |
| LOG_VERB( |
| "LE Scan request ignored by extended advertiser %d because the " |
| "scanning address %s(%hhx) does not match the target address %s(%hhx)", |
| advertiser.advertising_handle, |
| resolved_scanning_address.ToString().c_str(), |
| resolved_scanning_address.GetAddressType(), |
| advertiser.GetTargetAddress().ToString().c_str(), |
| advertiser.GetTargetAddress().GetAddressType()); |
| return; |
| } |
| |
| LOG_INFO( |
| "Accepting LE Scan request to extended advertiser %d from scanning " |
| "address %s(%hhx)", |
| advertiser.advertising_handle, |
| resolved_scanning_address.ToString().c_str(), |
| resolved_scanning_address.GetAddressType()); |
| |
| // Generate the SCAN_RSP packet. |
| // Note: If the advertiser processes the scan request, the advertiser’s |
| // device address (AdvA field) in the SCAN_RSP PDU shall be the same as |
| // the advertiser’s device address (AdvA field) in the SCAN_REQ PDU to |
| // which it is responding. |
| SendLeLinkLayerPacket( |
| model::packets::LeScanResponseBuilder::Create( |
| advertising_address.GetAddress(), scanning_address.GetAddress(), |
| static_cast<model::packets::AddressType>( |
| advertising_address.GetAddressType()), |
| advertiser.scan_response_data), |
| advertiser.advertising_tx_power); |
| } |
| |
| void LinkLayerController::IncomingLeScanPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| auto scan_request = model::packets::LeScanView::Create(incoming); |
| ASSERT(scan_request.IsValid()); |
| |
| AddressWithType scanning_address{ |
| scan_request.GetSourceAddress(), |
| static_cast<AddressType>(scan_request.GetScanningAddressType())}; |
| |
| AddressWithType advertising_address{ |
| scan_request.GetDestinationAddress(), |
| static_cast<AddressType>(scan_request.GetAdvertisingAddressType())}; |
| |
| // Note: Vol 6, Part B § 6.2 Privacy in the Advertising State. |
| // |
| // When an advertiser receives a scan request that contains a resolvable |
| // private address for the scanner’s device address (ScanA field) and |
| // address resolution is enabled, the Link Layer shall resolve the private |
| // address. The advertising filter policy shall then determine if |
| // the advertiser processes the scan request. |
| AddressWithType resolved_scanning_address = |
| ResolvePrivateAddress(scanning_address, IrkSelection::Peer) |
| .value_or(scanning_address); |
| |
| if (resolved_scanning_address != scanning_address) { |
| LOG_VERB("Resolved the scanning address %s(%hhx) to %s(%hhx)", |
| scanning_address.ToString().c_str(), |
| scanning_address.GetAddressType(), |
| resolved_scanning_address.ToString().c_str(), |
| resolved_scanning_address.GetAddressType()); |
| } |
| |
| ProcessIncomingLegacyScanRequest(scanning_address, resolved_scanning_address, |
| advertising_address); |
| for (auto& [_, advertiser] : extended_advertisers_) { |
| ProcessIncomingExtendedScanRequest(advertiser, scanning_address, |
| resolved_scanning_address, |
| advertising_address); |
| } |
| } |
| |
| void LinkLayerController::IncomingLeScanResponsePacket( |
| model::packets::LinkLayerPacketView incoming, uint8_t rssi) { |
| auto scan_response = model::packets::LeScanResponseView::Create(incoming); |
| ASSERT(scan_response.IsValid()); |
| |
| if (!scanner_.IsEnabled()) { |
| return; |
| } |
| |
| if (!scanner_.pending_scan_request) { |
| LOG_VERB( |
| "LE Scan response ignored by scanner because no request is currently " |
| "pending"); |
| return; |
| } |
| |
| AddressWithType advertising_address{ |
| scan_response.GetSourceAddress(), |
| static_cast<AddressType>(scan_response.GetAdvertisingAddressType())}; |
| |
| // If the advertiser processes the scan request, the advertiser’s device |
| // address (AdvA field) in the scan response PDU shall be the same as the |
| // advertiser’s device address (AdvA field) in the scan request PDU to which |
| // it is responding. |
| if (advertising_address != scanner_.pending_scan_request) { |
| LOG_VERB( |
| "LE Scan response ignored by scanner because the advertising address " |
| "%s(%hhx) does not match the pending request %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| scanner_.pending_scan_request.value().ToString().c_str(), |
| scanner_.pending_scan_request.value().GetAddressType()); |
| return; |
| } |
| |
| AddressWithType resolved_advertising_address = |
| ResolvePrivateAddress(advertising_address, IrkSelection::Peer) |
| .value_or(advertising_address); |
| |
| if (advertising_address != resolved_advertising_address) { |
| LOG_VERB("Resolved the advertising address %s(%hhx) to %s(%hhx)", |
| advertising_address.ToString().c_str(), |
| advertising_address.GetAddressType(), |
| resolved_advertising_address.ToString().c_str(), |
| resolved_advertising_address.GetAddressType()); |
| } |
| |
| LOG_INFO("Accepting LE Scan response from advertising address %s(%hhx)", |
| resolved_advertising_address.ToString().c_str(), |
| resolved_advertising_address.GetAddressType()); |
| |
| scanner_.pending_scan_request = {}; |
| |
| bool should_send_advertising_report = true; |
| if (scanner_.filter_duplicates != |
| bluetooth::hci::FilterDuplicates::DISABLED) { |
| if (scanner_.IsPacketInHistory(incoming)) { |
| should_send_advertising_report = false; |
| } else { |
| scanner_.AddPacketToHistory(incoming); |
| } |
| } |
| |
| if (LegacyAdvertising() && should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::ADVERTISING_REPORT)) { |
| bluetooth::hci::LeAdvertisingResponseRaw response; |
| response.event_type_ = bluetooth::hci::AdvertisingEventType::SCAN_RESPONSE; |
| response.address_ = resolved_advertising_address.GetAddress(); |
| response.address_type_ = resolved_advertising_address.GetAddressType(); |
| response.advertising_data_ = scan_response.GetScanResponseData(); |
| response.rssi_ = rssi; |
| send_event_( |
| bluetooth::hci::LeAdvertisingReportRawBuilder::Create({response})); |
| } |
| |
| if (ExtendedAdvertising() && should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) { |
| bluetooth::hci::LeExtendedAdvertisingResponseRaw response; |
| response.address_ = resolved_advertising_address.GetAddress(); |
| response.address_type_ = |
| static_cast<bluetooth::hci::DirectAdvertisingAddressType>( |
| resolved_advertising_address.GetAddressType()); |
| response.connectable_ = scanner_.connectable_scan_response; |
| response.scannable_ = true; |
| response.legacy_ = true; |
| response.scan_response_ = true; |
| response.primary_phy_ = bluetooth::hci::PrimaryPhyType::LE_1M; |
| response.advertising_sid_ = 0xFF; |
| response.tx_power_ = 0x7F; |
| response.advertising_data_ = scan_response.GetScanResponseData(); |
| response.rssi_ = rssi; |
| send_event_(bluetooth::hci::LeExtendedAdvertisingReportRawBuilder::Create( |
| {response})); |
| } |
| } |
| |
| void LinkLayerController::LeScanning() { |
| if (!scanner_.IsEnabled()) { |
| return; |
| } |
| |
| std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); |
| |
| // Extended Scanning 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 (scanner_.timeout.has_value() && |
| !scanner_.periodical_timeout.has_value() && |
| now >= scanner_.timeout.value()) { |
| // At the end of a single scan (Duration non-zero but Period zero), |
| // an HCI_LE_Scan_Timeout event shall be generated. |
| LOG_INFO("Extended Scan Timeout"); |
| scanner_.scan_enable = false; |
| scanner_.history.clear(); |
| if (IsLeEventUnmasked(SubeventCode::SCAN_TIMEOUT)) { |
| send_event_(bluetooth::hci::LeScanTimeoutBuilder::Create()); |
| } |
| } |
| |
| // End of duration with scan enabled |
| if (scanner_.timeout.has_value() && scanner_.periodical_timeout.has_value() && |
| now >= scanner_.timeout.value()) { |
| scanner_.timeout = {}; |
| } |
| |
| // End of period |
| if (!scanner_.timeout.has_value() && |
| scanner_.periodical_timeout.has_value() && |
| now >= scanner_.periodical_timeout.value()) { |
| if (scanner_.filter_duplicates == FilterDuplicates::RESET_EACH_PERIOD) { |
| scanner_.history.clear(); |
| } |
| scanner_.timeout = now + scanner_.duration; |
| scanner_.periodical_timeout = now + scanner_.period; |
| } |
| } |
| |
| void LinkLayerController::LeSynchronization() { |
| std::vector<uint16_t> removed_sync_handles; |
| for (auto& [_, sync] : synchronized_) { |
| if (sync.timeout > std::chrono::steady_clock::now()) { |
| LOG_INFO("Periodic advertising sync with handle 0x%x lost", |
| sync.sync_handle); |
| removed_sync_handles.push_back(sync.sync_handle); |
| } |
| if (IsLeEventUnmasked(SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST)) { |
| send_event_(bluetooth::hci::LePeriodicAdvertisingSyncLostBuilder::Create( |
| sync.sync_handle)); |
| } |
| } |
| |
| for (auto sync_handle : removed_sync_handles) { |
| synchronized_.erase(sync_handle); |
| } |
| } |
| |
| void LinkLayerController::IncomingPagePacket( |
| model::packets::LinkLayerPacketView incoming) { |
| auto page = model::packets::PageView::Create(incoming); |
| ASSERT(page.IsValid()); |
| LOG_INFO("from %s", incoming.GetSourceAddress().ToString().c_str()); |
| |
| bool allow_role_switch = page.GetAllowRoleSwitch(); |
| if (!connections_.CreatePendingConnection( |
| incoming.GetSourceAddress(), |
| authentication_enable_ == AuthenticationEnable::REQUIRED, |
| allow_role_switch)) { |
| // Send a response to indicate that we're busy, or drop the packet? |
| LOG_WARN("Failed to create a pending connection for %s", |
| incoming.GetSourceAddress().ToString().c_str()); |
| } |
| |
| bluetooth::hci::Address source_address{}; |
| bluetooth::hci::Address::FromString(page.GetSourceAddress().ToString(), |
| source_address); |
| |
| if (IsEventUnmasked(EventCode::CONNECTION_REQUEST)) { |
| send_event_(bluetooth::hci::ConnectionRequestBuilder::Create( |
| source_address, page.GetClassOfDevice(), |
| bluetooth::hci::ConnectionRequestLinkType::ACL)); |
| } |
| } |
| |
| void LinkLayerController::IncomingPageRejectPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| LOG_INFO("%s", incoming.GetSourceAddress().ToString().c_str()); |
| auto reject = model::packets::PageRejectView::Create(incoming); |
| ASSERT(reject.IsValid()); |
| LOG_INFO("Sending CreateConnectionComplete"); |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| static_cast<ErrorCode>(reject.GetReason()), 0x0eff, |
| incoming.GetSourceAddress(), bluetooth::hci::LinkType::ACL, |
| bluetooth::hci::Enable::DISABLED)); |
| } |
| } |
| |
| void LinkLayerController::IncomingPageResponsePacket( |
| model::packets::LinkLayerPacketView incoming) { |
| Address peer = incoming.GetSourceAddress(); |
| LOG_INFO("%s", peer.ToString().c_str()); |
| uint16_t handle = |
| connections_.CreateConnection(peer, incoming.GetDestinationAddress()); |
| if (handle == kReservedHandle) { |
| LOG_WARN("No free handles"); |
| return; |
| } |
| CancelScheduledTask(page_timeout_task_id_); |
| ASSERT(link_manager_add_link( |
| lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(peer.data()))); |
| |
| CheckExpiringConnection(handle); |
| |
| auto addr = incoming.GetSourceAddress(); |
| auto response = model::packets::PageResponseView::Create(incoming); |
| ASSERT(response.IsValid()); |
| /* Role change event before connection complete is a quirk commonly exists in |
| * Android-capatable Bluetooth controllers. |
| * On the initiator side, only connection in peripheral role should be |
| * accompanied with a role change event */ |
| // TODO(b/274476773): Add a config option for this quirk |
| if (connections_.IsRoleSwitchAllowedForPendingConnection() && |
| response.GetTryRoleSwitch()) { |
| auto role = bluetooth::hci::Role::PERIPHERAL; |
| connections_.SetAclRole(handle, role); |
| if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, |
| addr, role)); |
| } |
| } |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, addr, bluetooth::hci::LinkType::ACL, |
| bluetooth::hci::Enable::DISABLED)); |
| } |
| } |
| |
| void LinkLayerController::Tick() { |
| RunPendingTasks(); |
| if (inquiry_timer_task_id_ != kInvalidTaskId) { |
| Inquiry(); |
| } |
| LeAdvertising(); |
| LeScanning(); |
| link_manager_tick(lm_.get()); |
| } |
| |
| void LinkLayerController::Close() { |
| for (auto handle : connections_.GetAclHandles()) { |
| Disconnect(handle, ErrorCode::CONNECTION_TIMEOUT, |
| ErrorCode::CONNECTION_TIMEOUT); |
| } |
| } |
| |
| void LinkLayerController::RegisterEventChannel( |
| const std::function<void(std::shared_ptr<bluetooth::hci::EventBuilder>)>& |
| send_event) { |
| send_event_ = send_event; |
| } |
| |
| void LinkLayerController::RegisterAclChannel( |
| const std::function<void(std::shared_ptr<bluetooth::hci::AclBuilder>)>& |
| send_acl) { |
| send_acl_ = send_acl; |
| } |
| |
| void LinkLayerController::RegisterScoChannel( |
| const std::function<void(std::shared_ptr<bluetooth::hci::ScoBuilder>)>& |
| send_sco) { |
| send_sco_ = send_sco; |
| } |
| |
| void LinkLayerController::RegisterIsoChannel( |
| const std::function<void(std::shared_ptr<bluetooth::hci::IsoBuilder>)>& |
| send_iso) { |
| send_iso_ = send_iso; |
| } |
| |
| void LinkLayerController::RegisterRemoteChannel( |
| const std::function< |
| void(std::shared_ptr<model::packets::LinkLayerPacketBuilder>, Phy::Type, |
| int8_t)>& send_to_remote) { |
| send_to_remote_ = send_to_remote; |
| } |
| |
| void LinkLayerController::ForwardToLm(bluetooth::hci::CommandView command) { |
| auto packet = std::vector(command.begin(), command.end()); |
| ASSERT(link_manager_ingest_hci(lm_.get(), packet.data(), packet.size())); |
| } |
| |
| std::vector<bluetooth::hci::Lap> const& LinkLayerController::ReadCurrentIacLap() |
| const { |
| return current_iac_lap_list_; |
| } |
| |
| void LinkLayerController::WriteCurrentIacLap( |
| std::vector<bluetooth::hci::Lap> iac_lap) { |
| current_iac_lap_list_.swap(iac_lap); |
| |
| // If Num_Current_IAC is greater than Num_Supported_IAC then only the first |
| // Num_Supported_IAC shall be stored in the Controller |
| if (current_iac_lap_list_.size() > properties_.num_supported_iac) { |
| current_iac_lap_list_.resize(properties_.num_supported_iac); |
| } |
| } |
| |
| ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& bd_addr, |
| bool try_role_switch) { |
| if (connections_.HasPendingConnection(bd_addr)) { |
| LOG_INFO("Accepting connection request from %s", |
| bd_addr.ToString().c_str()); |
| ScheduleTask(kNoDelayMs, [this, bd_addr, try_role_switch]() { |
| LOG_INFO("Accepted connection from %s", bd_addr.ToString().c_str()); |
| MakePeripheralConnection(bd_addr, try_role_switch); |
| }); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| // The HCI command Accept Connection may be used to accept incoming SCO |
| // connection requests. |
| if (connections_.HasPendingScoConnection(bd_addr)) { |
| ErrorCode status = ErrorCode::SUCCESS; |
| uint16_t sco_handle = 0; |
| ScoLinkParameters link_parameters = {}; |
| ScoConnectionParameters connection_parameters = |
| connections_.GetScoConnectionParameters(bd_addr); |
| |
| if (!connections_.AcceptPendingScoConnection( |
| bd_addr, connection_parameters, [this, bd_addr] { |
| return LinkLayerController::StartScoStream(bd_addr); |
| })) { |
| connections_.CancelPendingScoConnection(bd_addr); |
| status = ErrorCode::SCO_INTERVAL_REJECTED; // TODO: proper status code |
| } else { |
| sco_handle = connections_.GetScoHandle(bd_addr); |
| link_parameters = connections_.GetScoLinkParameters(bd_addr); |
| } |
| |
| // Send eSCO connection response to peer. |
| SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create( |
| GetAddress(), bd_addr, (uint8_t)status, |
| link_parameters.transmission_interval, |
| link_parameters.retransmission_window, link_parameters.rx_packet_length, |
| link_parameters.tx_packet_length, link_parameters.air_mode, |
| link_parameters.extended)); |
| |
| // Schedule HCI Connection Complete event. |
| ScheduleTask(kNoDelayMs, [this, status, sco_handle, bd_addr]() { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode(status), sco_handle, bd_addr, bluetooth::hci::LinkType::SCO, |
| bluetooth::hci::Enable::DISABLED)); |
| }); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| LOG_INFO("No pending connection for %s", bd_addr.ToString().c_str()); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| void LinkLayerController::MakePeripheralConnection(const Address& addr, |
| bool try_role_switch) { |
| LOG_INFO("Sending page response to %s", addr.ToString().c_str()); |
| SendLinkLayerPacket(model::packets::PageResponseBuilder::Create( |
| GetAddress(), addr, try_role_switch)); |
| |
| uint16_t handle = connections_.CreateConnection(addr, GetAddress()); |
| if (handle == kReservedHandle) { |
| LOG_INFO("CreateConnection failed"); |
| return; |
| } |
| ASSERT(link_manager_add_link( |
| lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(addr.data()))); |
| |
| CheckExpiringConnection(handle); |
| |
| /* Role change event before connection complete is a quirk commonly exists in |
| * Android-capatable Bluetooth controllers. |
| * On the responder side, any connection should be accompanied with a role |
| * change event */ |
| // TODO(b/274476773): Add a config option for this quirk |
| auto role = |
| try_role_switch && connections_.IsRoleSwitchAllowedForPendingConnection() |
| ? bluetooth::hci::Role::CENTRAL |
| : bluetooth::hci::Role::PERIPHERAL; |
| connections_.SetAclRole(handle, role); |
| if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, |
| addr, role)); |
| } |
| |
| LOG_INFO("CreateConnection returned handle 0x%x", handle); |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, addr, bluetooth::hci::LinkType::ACL, |
| bluetooth::hci::Enable::DISABLED)); |
| } |
| } |
| |
| ErrorCode LinkLayerController::RejectConnectionRequest(const Address& addr, |
| uint8_t reason) { |
| if (!connections_.HasPendingConnection(addr)) { |
| LOG_INFO("No pending connection for %s", addr.ToString().c_str()); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| ScheduleTask(kNoDelayMs, [this, addr, reason]() { |
| RejectPeripheralConnection(addr, reason); |
| }); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::RejectPeripheralConnection(const Address& addr, |
| uint8_t reason) { |
| LOG_INFO("Sending page reject to %s (reason 0x%02hhx)", |
| addr.ToString().c_str(), reason); |
| SendLinkLayerPacket( |
| model::packets::PageRejectBuilder::Create(GetAddress(), addr, reason)); |
| |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| static_cast<ErrorCode>(reason), 0xeff, addr, |
| bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); |
| } |
| } |
| |
| ErrorCode LinkLayerController::CreateConnection(const Address& addr, |
| uint16_t /* packet_type */, |
| uint8_t /* page_scan_mode */, |
| uint16_t /* clock_offset */, |
| uint8_t allow_role_switch) { |
| if (!connections_.CreatePendingConnection( |
| addr, authentication_enable_ == AuthenticationEnable::REQUIRED, |
| allow_role_switch)) { |
| return ErrorCode::CONTROLLER_BUSY; |
| } |
| |
| page_timeout_task_id_ = ScheduleTask( |
| duration_cast<milliseconds>(page_timeout_ * microseconds(625)), |
| [this, addr] { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::PAGE_TIMEOUT, 0xeff, addr, bluetooth::hci::LinkType::ACL, |
| bluetooth::hci::Enable::DISABLED)); |
| }); |
| |
| SendLinkLayerPacket(model::packets::PageBuilder::Create( |
| GetAddress(), addr, class_of_device_, allow_role_switch)); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::CreateConnectionCancel(const Address& addr) { |
| if (!connections_.CancelPendingConnection(addr)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| CancelScheduledTask(page_timeout_task_id_); |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::SendDisconnectionCompleteEvent(uint16_t handle, |
| ErrorCode reason) { |
| if (IsEventUnmasked(EventCode::DISCONNECTION_COMPLETE)) { |
| ScheduleTask(kNoDelayMs, [this, handle, reason]() { |
| send_event_(bluetooth::hci::DisconnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, reason)); |
| }); |
| } |
| } |
| |
| ErrorCode LinkLayerController::Disconnect(uint16_t handle, |
| ErrorCode host_reason, |
| ErrorCode controller_reason) { |
| if (connections_.HasScoHandle(handle)) { |
| const Address remote = connections_.GetScoAddress(handle); |
| LOG_INFO("Disconnecting eSCO connection with %s", |
| remote.ToString().c_str()); |
| |
| SendLinkLayerPacket(model::packets::ScoDisconnectBuilder::Create( |
| GetAddress(), remote, static_cast<uint8_t>(host_reason))); |
| |
| connections_.Disconnect( |
| handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); |
| SendDisconnectionCompleteEvent(handle, controller_reason); |
| return ErrorCode::SUCCESS; |
| } |
| |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| const AddressWithType remote = connections_.GetAddress(handle); |
| auto is_br_edr = connections_.GetPhyType(handle) == Phy::Type::BR_EDR; |
| |
| if (is_br_edr) { |
| LOG_INFO("Disconnecting ACL connection with %s", remote.ToString().c_str()); |
| |
| uint16_t sco_handle = connections_.GetScoHandle(remote.GetAddress()); |
| if (sco_handle != kReservedHandle) { |
| SendLinkLayerPacket(model::packets::ScoDisconnectBuilder::Create( |
| GetAddress(), remote.GetAddress(), |
| static_cast<uint8_t>(host_reason))); |
| |
| connections_.Disconnect( |
| sco_handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); |
| SendDisconnectionCompleteEvent(sco_handle, controller_reason); |
| } |
| |
| SendLinkLayerPacket(model::packets::DisconnectBuilder::Create( |
| GetAddress(), remote.GetAddress(), static_cast<uint8_t>(host_reason))); |
| } else { |
| LOG_INFO("Disconnecting LE connection with %s", remote.ToString().c_str()); |
| |
| SendLeLinkLayerPacket(model::packets::DisconnectBuilder::Create( |
| connections_.GetOwnAddress(handle).GetAddress(), remote.GetAddress(), |
| static_cast<uint8_t>(host_reason))); |
| } |
| |
| connections_.Disconnect( |
| handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); |
| SendDisconnectionCompleteEvent(handle, controller_reason); |
| if (is_br_edr) { |
| ASSERT(link_manager_remove_link( |
| lm_.get(), |
| reinterpret_cast<uint8_t(*)[6]>(remote.GetAddress().data()))); |
| } |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::ChangeConnectionPacketType(uint16_t handle, |
| uint16_t types) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| ScheduleTask(kNoDelayMs, [this, handle, types]() { |
| if (IsEventUnmasked(EventCode::CONNECTION_PACKET_TYPE_CHANGED)) { |
| send_event_(bluetooth::hci::ConnectionPacketTypeChangedBuilder::Create( |
| ErrorCode::SUCCESS, handle, types)); |
| } |
| }); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) |
| ErrorCode LinkLayerController::ChangeConnectionLinkKey(uint16_t handle) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // TODO: implement real logic |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) |
| ErrorCode LinkLayerController::CentralLinkKey(uint8_t /* key_flag */) { |
| // TODO: implement real logic |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| ErrorCode LinkLayerController::HoldMode(uint16_t handle, |
| uint16_t hold_mode_max_interval, |
| uint16_t hold_mode_min_interval) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| if (hold_mode_max_interval < hold_mode_min_interval) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // TODO: implement real logic |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| ErrorCode LinkLayerController::SniffMode(uint16_t handle, |
| uint16_t sniff_max_interval, |
| uint16_t sniff_min_interval, |
| uint16_t sniff_attempt, |
| uint16_t sniff_timeout) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| if (sniff_max_interval < sniff_min_interval || sniff_attempt < 0x0001 || |
| sniff_attempt > 0x7FFF || sniff_timeout > 0x7FFF) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // TODO: implement real logic |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| ErrorCode LinkLayerController::ExitSniffMode(uint16_t handle) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // TODO: implement real logic |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| ErrorCode LinkLayerController::QosSetup(uint16_t handle, uint8_t service_type, |
| uint32_t /* token_rate */, |
| uint32_t /* peak_bandwidth */, |
| uint32_t /* latency */, |
| uint32_t /* delay_variation */) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| if (service_type > 0x02) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // TODO: implement real logic |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| ErrorCode LinkLayerController::RoleDiscovery(uint16_t handle, |
| bluetooth::hci::Role* role) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| *role = connections_.GetAclRole(handle); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::SwitchRole(Address addr, |
| bluetooth::hci::Role role) { |
| auto handle = connections_.GetHandleOnlyAddress(addr); |
| if (handle == rootcanal::kReservedHandle) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| // TODO(b/274248798): Reject role switch if disabled in link policy |
| SendLinkLayerPacket(model::packets::RoleSwitchRequestBuilder::Create( |
| GetAddress(), addr, static_cast<uint8_t>(role))); |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::IncomingRoleSwitchRequest( |
| model::packets::LinkLayerPacketView incoming) { |
| auto addr = incoming.GetSourceAddress(); |
| auto handle = connections_.GetHandleOnlyAddress(addr); |
| auto request = model::packets::RoleSwitchRequestView::Create(incoming); |
| ASSERT(request.IsValid()); |
| |
| // TODO(b/274248798): Reject role switch if disabled in link policy |
| Role remote_role = static_cast<Role>(request.GetInitiatorNewRole()); |
| Role local_role = |
| remote_role == Role::CENTRAL ? Role::PERIPHERAL : Role::CENTRAL; |
| connections_.SetAclRole(handle, local_role); |
| if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| ScheduleTask(kNoDelayMs, [this, addr, local_role]() { |
| send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, |
| addr, local_role)); |
| }); |
| } |
| ScheduleTask(kNoDelayMs, [this, addr, remote_role]() { |
| SendLinkLayerPacket(model::packets::RoleSwitchResponseBuilder::Create( |
| GetAddress(), addr, static_cast<uint8_t>(ErrorCode::SUCCESS), |
| static_cast<uint8_t>(remote_role))); |
| }); |
| } |
| |
| void LinkLayerController::IncomingRoleSwitchResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto addr = incoming.GetSourceAddress(); |
| auto handle = connections_.GetHandleOnlyAddress(addr); |
| auto response = model::packets::RoleSwitchResponseView::Create(incoming); |
| ASSERT(response.IsValid()); |
| |
| // TODO(b/274248798): Reject role switch if disabled in link policy |
| ErrorCode status = ErrorCode::SUCCESS; |
| Role role = static_cast<Role>(response.GetInitiatorNewRole()); |
| if (response.GetStatus() == static_cast<uint8_t>(ErrorCode::SUCCESS)) { |
| connections_.SetAclRole(handle, role); |
| } else { |
| status = static_cast<ErrorCode>(response.GetStatus()); |
| } |
| |
| if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| ScheduleTask(kNoDelayMs, [this, status, addr, role]() { |
| send_event_( |
| bluetooth::hci::RoleChangeBuilder::Create(status, addr, role)); |
| }); |
| } |
| } |
| |
| ErrorCode LinkLayerController::ReadLinkPolicySettings(uint16_t handle, |
| uint16_t* settings) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| *settings = connections_.GetAclLinkPolicySettings(handle); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::WriteLinkPolicySettings(uint16_t handle, |
| uint16_t settings) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| if (settings > 7 /* Sniff + Hold + Role switch */) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| connections_.SetAclLinkPolicySettings(handle, settings); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::WriteDefaultLinkPolicySettings( |
| uint16_t settings) { |
| if (settings > 7 /* Sniff + Hold + Role switch */) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| default_link_policy_settings_ = settings; |
| return ErrorCode::SUCCESS; |
| } |
| |
| uint16_t LinkLayerController::ReadDefaultLinkPolicySettings() const { |
| return default_link_policy_settings_; |
| } |
| |
| void LinkLayerController::ReadLocalOobData() { |
| std::array<uint8_t, 16> c_array( |
| {'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', '0', |
| static_cast<uint8_t>((oob_id_ % 0x10000) >> 8), |
| static_cast<uint8_t>(oob_id_ % 0x100)}); |
| |
| std::array<uint8_t, 16> r_array( |
| {'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', '0', |
| static_cast<uint8_t>((oob_id_ % 0x10000) >> 8), |
| static_cast<uint8_t>(oob_id_ % 0x100)}); |
| |
| send_event_(bluetooth::hci::ReadLocalOobDataCompleteBuilder::Create( |
| 1, ErrorCode::SUCCESS, c_array, r_array)); |
| oob_id_ += 1; |
| } |
| |
| void LinkLayerController::ReadLocalOobExtendedData() { |
| std::array<uint8_t, 16> c_192_array( |
| {'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', '0', '0', |
| static_cast<uint8_t>((oob_id_ % 0x10000) >> 8), |
| static_cast<uint8_t>(oob_id_ % 0x100)}); |
| |
| std::array<uint8_t, 16> r_192_array( |
| {'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', '0', '0', |
| static_cast<uint8_t>((oob_id_ % 0x10000) >> 8), |
| static_cast<uint8_t>(oob_id_ % 0x100)}); |
| |
| std::array<uint8_t, 16> c_256_array( |
| {'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', '0', '0', |
| static_cast<uint8_t>((oob_id_ % 0x10000) >> 8), |
| static_cast<uint8_t>(oob_id_ % 0x100)}); |
| |
| std::array<uint8_t, 16> r_256_array( |
| {'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', '0', '0', |
| static_cast<uint8_t>((oob_id_ % 0x10000) >> 8), |
| static_cast<uint8_t>(oob_id_ % 0x100)}); |
| |
| send_event_(bluetooth::hci::ReadLocalOobExtendedDataCompleteBuilder::Create( |
| 1, ErrorCode::SUCCESS, c_192_array, r_192_array, c_256_array, |
| r_256_array)); |
| oob_id_ += 1; |
| } |
| |
| ErrorCode LinkLayerController::FlowSpecification( |
| uint16_t handle, uint8_t flow_direction, uint8_t service_type, |
| uint32_t /* token_rate */, uint32_t /* token_bucket_size */, |
| uint32_t /* peak_bandwidth */, uint32_t /* access_latency */) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| if (flow_direction > 0x01 || service_type > 0x02) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| // TODO: implement real logic |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| ErrorCode LinkLayerController::WriteLinkSupervisionTimeout( |
| uint16_t handle, uint16_t /* timeout */) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::LeConnectionUpdateComplete( |
| uint16_t handle, uint16_t interval_min, uint16_t interval_max, |
| uint16_t latency, uint16_t supervision_timeout) { |
| ErrorCode status = ErrorCode::SUCCESS; |
| if (!connections_.HasHandle(handle)) { |
| status = ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| if (interval_min < 6 || interval_max > 0xC80 || interval_min > interval_max || |
| interval_max < interval_min || latency > 0x1F3 || |
| supervision_timeout < 0xA || supervision_timeout > 0xC80 || |
| // The Supervision_Timeout in milliseconds (*10) shall be larger than (1 + |
| // Connection_Latency) * Connection_Interval_Max (* 5/4) * 2 |
| supervision_timeout <= ((((1 + latency) * interval_max * 10) / 4) / 10)) { |
| status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| uint16_t interval = (interval_min + interval_max) / 2; |
| |
| SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create( |
| connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress(), |
| static_cast<uint8_t>(ErrorCode::SUCCESS), interval, latency, |
| supervision_timeout)); |
| |
| if (IsLeEventUnmasked(SubeventCode::CONNECTION_UPDATE_COMPLETE)) { |
| send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create( |
| status, handle, interval, latency, supervision_timeout)); |
| } |
| } |
| |
| ErrorCode LinkLayerController::LeConnectionUpdate( |
| uint16_t handle, uint16_t interval_min, uint16_t interval_max, |
| uint16_t latency, uint16_t supervision_timeout) { |
| if (!connections_.HasHandle(handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| bluetooth::hci::Role role = connections_.GetAclRole(handle); |
| |
| if (role == bluetooth::hci::Role::CENTRAL) { |
| // As Central, it is allowed to directly send |
| // LL_CONNECTION_PARAM_UPDATE_IND to update the parameters. |
| SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create( |
| connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress(), |
| static_cast<uint8_t>(ErrorCode::SUCCESS), interval_max, latency, |
| supervision_timeout)); |
| |
| if (IsLeEventUnmasked(SubeventCode::CONNECTION_UPDATE_COMPLETE)) { |
| send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle, interval_max, latency, |
| supervision_timeout)); |
| } |
| } else { |
| // Send LL_CONNECTION_PARAM_REQ and wait for LL_CONNECTION_PARAM_RSP |
| // in return. |
| SendLeLinkLayerPacket(LeConnectionParameterRequestBuilder::Create( |
| connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress(), interval_min, |
| interval_max, latency, supervision_timeout)); |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::LeRemoteConnectionParameterRequestReply( |
| uint16_t connection_handle, uint16_t interval_min, uint16_t interval_max, |
| uint16_t timeout, uint16_t latency, uint16_t minimum_ce_length, |
| uint16_t maximum_ce_length) { |
| if (!connections_.HasHandle(connection_handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| if ((interval_min > interval_max) || |
| (minimum_ce_length > maximum_ce_length)) { |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| ScheduleTask(kNoDelayMs, [this, connection_handle, interval_min, interval_max, |
| latency, timeout]() { |
| LeConnectionUpdateComplete(connection_handle, interval_min, interval_max, |
| latency, timeout); |
| }); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::LeRemoteConnectionParameterRequestNegativeReply( |
| uint16_t connection_handle, bluetooth::hci::ErrorCode reason) { |
| if (!connections_.HasHandle(connection_handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| uint16_t interval = 0; |
| uint16_t latency = 0; |
| uint16_t timeout = 0; |
| SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create( |
| connections_.GetOwnAddress(connection_handle).GetAddress(), |
| connections_.GetAddress(connection_handle).GetAddress(), |
| static_cast<uint8_t>(reason), interval, latency, timeout)); |
| return ErrorCode::SUCCESS; |
| } |
| |
| bool LinkLayerController::HasAclConnection() { |
| return !connections_.GetAclHandles().empty(); |
| } |
| |
| bool LinkLayerController::HasAclConnection(uint16_t connection_handle) { |
| return connections_.HasHandle(connection_handle); |
| } |
| |
| void LinkLayerController::LeReadIsoTxSync(uint16_t /* handle */) {} |
| |
| void LinkLayerController::LeSetCigParameters( |
| uint8_t cig_id, uint32_t sdu_interval_m_to_s, uint32_t sdu_interval_s_to_m, |
| bluetooth::hci::ClockAccuracy clock_accuracy, |
| bluetooth::hci::Packing packing, bluetooth::hci::Enable framing, |
| uint16_t max_transport_latency_m_to_s, |
| uint16_t max_transport_latency_s_to_m, |
| std::vector<bluetooth::hci::CisParametersConfig> cis_config) { |
| if (IsEventUnmasked(EventCode::LE_META_EVENT)) { |
| send_event_(connections_.SetCigParameters( |
| cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, clock_accuracy, |
| packing, framing, max_transport_latency_m_to_s, |
| max_transport_latency_s_to_m, cis_config)); |
| } |
| } |
| |
| ErrorCode LinkLayerController::LeCreateCis( |
| std::vector<bluetooth::hci::CreateCisConfig> cis_config) { |
| if (connections_.HasPendingCis()) { |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| for (auto& config : cis_config) { |
| if (!connections_.HasHandle(config.acl_connection_handle_)) { |
| LOG_INFO("Unknown ACL handle %04x", config.acl_connection_handle_); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| if (!connections_.HasCisHandle(config.cis_connection_handle_)) { |
| LOG_INFO("Unknown CIS handle %04x", config.cis_connection_handle_); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| } |
| for (auto& config : cis_config) { |
| connections_.CreatePendingCis(config); |
| auto own_address = |
| connections_.GetOwnAddress(config.acl_connection_handle_); |
| auto peer_address = connections_.GetAddress(config.acl_connection_handle_); |
| StreamParameters stream_parameters = |
| connections_.GetStreamParameters(config.cis_connection_handle_); |
| GroupParameters group_parameters = |
| connections_.GetGroupParameters(stream_parameters.group_id); |
| |
| SendLeLinkLayerPacket(model::packets::IsoConnectionRequestBuilder::Create( |
| own_address.GetAddress(), peer_address.GetAddress(), |
| stream_parameters.group_id, group_parameters.sdu_interval_m_to_s, |
| group_parameters.sdu_interval_s_to_m, group_parameters.interleaved, |
| group_parameters.framed, group_parameters.max_transport_latency_m_to_s, |
| group_parameters.max_transport_latency_s_to_m, |
| stream_parameters.stream_id, stream_parameters.max_sdu_m_to_s, |
| stream_parameters.max_sdu_s_to_m, config.cis_connection_handle_, |
| config.acl_connection_handle_)); |
| } |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::LeRemoveCig(uint8_t cig_id) { |
| return connections_.RemoveCig(cig_id); |
| } |
| |
| ErrorCode LinkLayerController::LeAcceptCisRequest(uint16_t cis_handle) { |
| if (!connections_.HasPendingCisConnection(cis_handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| auto acl_handle = connections_.GetPendingAclHandle(cis_handle); |
| |
| connections_.ConnectCis(cis_handle); |
| |
| SendLeLinkLayerPacket(model::packets::IsoConnectionResponseBuilder::Create( |
| connections_.GetOwnAddress(acl_handle).GetAddress(), |
| connections_.GetAddress(acl_handle).GetAddress(), |
| static_cast<uint8_t>(ErrorCode::SUCCESS), cis_handle, acl_handle, |
| connections_.GetRemoteCisHandleForCisHandle(cis_handle))); |
| |
| // Both sides have to send LeCisEstablished event |
| |
| uint32_t cig_sync_delay = 0x100; |
| uint32_t cis_sync_delay = 0x200; |
| uint32_t latency_m_to_s = 0x200; |
| uint32_t latency_s_to_m = 0x200; |
| uint8_t nse = 1; |
| uint8_t bn_m_to_s = 0; |
| uint8_t bn_s_to_m = 0; |
| uint8_t ft_m_to_s = 0; |
| uint8_t ft_s_to_m = 0; |
| uint8_t max_pdu_m_to_s = 0x40; |
| uint8_t max_pdu_s_to_m = 0x40; |
| uint16_t iso_interval = 0x100; |
| if (IsEventUnmasked(EventCode::LE_META_EVENT)) { |
| send_event_(bluetooth::hci::LeCisEstablishedBuilder::Create( |
| ErrorCode::SUCCESS, cis_handle, cig_sync_delay, cis_sync_delay, |
| latency_m_to_s, latency_s_to_m, |
| bluetooth::hci::SecondaryPhyType::NO_PACKETS, |
| bluetooth::hci::SecondaryPhyType::NO_PACKETS, nse, bn_m_to_s, bn_s_to_m, |
| ft_m_to_s, ft_s_to_m, max_pdu_m_to_s, max_pdu_s_to_m, iso_interval)); |
| } |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::LeRejectCisRequest(uint16_t cis_handle, |
| ErrorCode reason) { |
| if (!connections_.HasPendingCisConnection(cis_handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| auto acl_handle = connections_.GetPendingAclHandle(cis_handle); |
| |
| SendLeLinkLayerPacket(model::packets::IsoConnectionResponseBuilder::Create( |
| connections_.GetOwnAddress(acl_handle).GetAddress(), |
| connections_.GetAddress(acl_handle).GetAddress(), |
| static_cast<uint8_t>(reason), cis_handle, acl_handle, kReservedHandle)); |
| connections_.RejectCis(cis_handle); |
| return ErrorCode::SUCCESS; |
| } |
| |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) |
| ErrorCode LinkLayerController::LeCreateBig( |
| uint8_t /* big_handle */, uint8_t /* advertising_handle */, |
| uint8_t /* num_bis */, uint32_t /* sdu_interval */, uint16_t /* max_sdu */, |
| uint16_t /* max_transport_latency */, uint8_t /* rtn */, |
| bluetooth::hci::SecondaryPhyType /* phy */, |
| bluetooth::hci::Packing /* packing */, bluetooth::hci::Enable /* framing */, |
| bluetooth::hci::Enable /* encryption */, |
| std::array<uint8_t, 16> /* broadcast_code */) { |
| return ErrorCode::SUCCESS; |
| } |
| |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) |
| ErrorCode LinkLayerController::LeTerminateBig(uint8_t /* big_handle */, |
| ErrorCode /* reason */) { |
| return ErrorCode::SUCCESS; |
| } |
| |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) |
| ErrorCode LinkLayerController::LeBigCreateSync( |
| uint8_t /* big_handle */, uint16_t /* sync_handle */, |
| bluetooth::hci::Enable /* encryption */, |
| std::array<uint8_t, 16> /* broadcast_code */, uint8_t /* mse */, |
| uint16_t /* big_sync_timeout */, std::vector<uint8_t> /* bis */) { |
| return ErrorCode::SUCCESS; |
| } |
| |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) |
| void LinkLayerController::LeBigTerminateSync(uint8_t /* big_handle */) {} |
| |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static) |
| ErrorCode LinkLayerController::LeRequestPeerSca(uint16_t /* request_handle */) { |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::LeSetupIsoDataPath( |
| uint16_t /* connection_handle */, |
| bluetooth::hci::DataPathDirection /* data_path_direction */, |
| uint8_t /* data_path_id */, uint64_t /* codec_id */, |
| uint32_t /* controller_Delay */, |
| std::vector<uint8_t> /* codec_configuration */) {} |
| |
| void LinkLayerController::LeRemoveIsoDataPath( |
| uint16_t /* connection_handle */, |
| bluetooth::hci::RemoveDataPathDirection /* remove_data_path_direction */) {} |
| |
| void LinkLayerController::HandleLeEnableEncryption( |
| uint16_t handle, std::array<uint8_t, 8> rand, uint16_t ediv, |
| std::array<uint8_t, kLtkSize> ltk) { |
| // TODO: Check keys |
| // TODO: Block ACL traffic or at least guard against it |
| if (!connections_.HasHandle(handle)) { |
| return; |
| } |
| SendLeLinkLayerPacket(model::packets::LeEncryptConnectionBuilder::Create( |
| connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress(), rand, ediv, ltk)); |
| } |
| |
| ErrorCode LinkLayerController::LeEnableEncryption( |
| uint16_t handle, std::array<uint8_t, 8> rand, uint16_t ediv, |
| std::array<uint8_t, kLtkSize> ltk) { |
| if (!connections_.HasHandle(handle)) { |
| LOG_INFO("Unknown handle %04x", handle); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| ScheduleTask(kNoDelayMs, [this, handle, rand, ediv, ltk]() { |
| HandleLeEnableEncryption(handle, rand, ediv, ltk); |
| }); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::LeLongTermKeyRequestReply( |
| uint16_t handle, std::array<uint8_t, kLtkSize> ltk) { |
| if (!connections_.HasHandle(handle)) { |
| LOG_INFO("Unknown handle %04x", handle); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // TODO: Check keys |
| if (connections_.IsEncrypted(handle)) { |
| if (IsEventUnmasked(EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE)) { |
| send_event_(bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create( |
| ErrorCode::SUCCESS, handle)); |
| } |
| } else { |
| connections_.Encrypt(handle); |
| if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) { |
| send_event_(bluetooth::hci::EncryptionChangeBuilder::Create( |
| ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::ON)); |
| } |
| } |
| SendLeLinkLayerPacket( |
| model::packets::LeEncryptConnectionResponseBuilder::Create( |
| connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress(), |
| std::array<uint8_t, 8>(), uint16_t(), ltk)); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::LeLongTermKeyRequestNegativeReply( |
| uint16_t handle) { |
| if (!connections_.HasHandle(handle)) { |
| LOG_INFO("Unknown handle %04x", handle); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| SendLeLinkLayerPacket( |
| model::packets::LeEncryptConnectionResponseBuilder::Create( |
| connections_.GetOwnAddress(handle).GetAddress(), |
| connections_.GetAddress(handle).GetAddress(), |
| std::array<uint8_t, 8>(), uint16_t(), std::array<uint8_t, 16>())); |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::Reset() { |
| host_supported_features_ = 0; |
| le_host_support_ = false; |
| secure_simple_pairing_host_support_ = false; |
| secure_connections_host_support_ = false; |
| le_host_supported_features_ = 0; |
| connected_isochronous_stream_host_support_ = false; |
| connection_subrating_host_support_ = false; |
| random_address_ = Address::kEmpty; |
| page_scan_enable_ = false; |
| inquiry_scan_enable_ = false; |
| inquiry_scan_interval_ = 0x1000; |
| inquiry_scan_window_ = 0x0012; |
| page_timeout_ = 0x2000; |
| connection_accept_timeout_ = 0x1FA0; |
| page_scan_interval_ = 0x0800; |
| page_scan_window_ = 0x0012; |
| voice_setting_ = 0x0060; |
| authentication_enable_ = AuthenticationEnable::NOT_REQUIRED; |
| default_link_policy_settings_ = 0x0000; |
| sco_flow_control_enable_ = false; |
| local_name_.fill(0); |
| extended_inquiry_response_.fill(0); |
| class_of_device_ = ClassOfDevice({0, 0, 0}); |
| min_encryption_key_size_ = 16; |
| event_mask_ = 0x00001fffffffffff; |
| event_mask_page_2_ = 0x0; |
| le_event_mask_ = 0x01f; |
| le_suggested_max_tx_octets_ = 0x001b; |
| le_suggested_max_tx_time_ = 0x0148; |
| resolvable_private_address_timeout_ = std::chrono::seconds(0x0384); |
| page_scan_repetition_mode_ = PageScanRepetitionMode::R0; |
| connections_ = AclConnectionHandler(); |
| oob_id_ = 1; |
| key_id_ = 1; |
| le_periodic_advertiser_list_.clear(); |
| le_filter_accept_list_.clear(); |
| le_resolving_list_.clear(); |
| le_resolving_list_enabled_ = false; |
| legacy_advertising_in_use_ = false; |
| extended_advertising_in_use_ = false; |
| legacy_advertiser_ = LegacyAdvertiser{}; |
| extended_advertisers_.clear(); |
| scanner_ = Scanner{}; |
| initiator_ = Initiator{}; |
| synchronizing_ = {}; |
| synchronized_ = {}; |
| last_inquiry_ = steady_clock::now(); |
| inquiry_mode_ = InquiryType::STANDARD; |
| inquiry_lap_ = 0; |
| inquiry_max_responses_ = 0; |
| default_tx_phys_ = properties_.LeSupportedPhys(); |
| default_rx_phys_ = properties_.LeSupportedPhys(); |
| |
| bluetooth::hci::Lap general_iac; |
| general_iac.lap_ = 0x33; // 0x9E8B33 |
| current_iac_lap_list_.clear(); |
| current_iac_lap_list_.emplace_back(general_iac); |
| |
| if (inquiry_timer_task_id_ != kInvalidTaskId) { |
| CancelScheduledTask(inquiry_timer_task_id_); |
| inquiry_timer_task_id_ = kInvalidTaskId; |
| } |
| |
| if (page_timeout_task_id_ != kInvalidTaskId) { |
| CancelScheduledTask(page_timeout_task_id_); |
| page_timeout_task_id_ = kInvalidTaskId; |
| } |
| |
| lm_.reset(link_manager_create(ops_)); |
| } |
| |
| void LinkLayerController::StartInquiry(milliseconds timeout) { |
| inquiry_timer_task_id_ = ScheduleTask(milliseconds(timeout), [this]() { |
| LinkLayerController::InquiryTimeout(); |
| }); |
| } |
| |
| void LinkLayerController::InquiryCancel() { |
| ASSERT(inquiry_timer_task_id_ != kInvalidTaskId); |
| CancelScheduledTask(inquiry_timer_task_id_); |
| inquiry_timer_task_id_ = kInvalidTaskId; |
| } |
| |
| void LinkLayerController::InquiryTimeout() { |
| if (inquiry_timer_task_id_ != kInvalidTaskId) { |
| inquiry_timer_task_id_ = kInvalidTaskId; |
| if (IsEventUnmasked(EventCode::INQUIRY_COMPLETE)) { |
| send_event_( |
| bluetooth::hci::InquiryCompleteBuilder::Create(ErrorCode::SUCCESS)); |
| } |
| } |
| } |
| |
| void LinkLayerController::SetInquiryMode(uint8_t mode) { |
| inquiry_mode_ = static_cast<model::packets::InquiryType>(mode); |
| } |
| |
| void LinkLayerController::SetInquiryLAP(uint64_t lap) { inquiry_lap_ = lap; } |
| |
| void LinkLayerController::SetInquiryMaxResponses(uint8_t max) { |
| inquiry_max_responses_ = max; |
| } |
| |
| void LinkLayerController::Inquiry() { |
| steady_clock::time_point now = steady_clock::now(); |
| if (duration_cast<milliseconds>(now - last_inquiry_) < milliseconds(2000)) { |
| return; |
| } |
| |
| SendLinkLayerPacket(model::packets::InquiryBuilder::Create( |
| GetAddress(), Address::kEmpty, inquiry_mode_, inquiry_lap_)); |
| last_inquiry_ = now; |
| } |
| |
| void LinkLayerController::SetInquiryScanEnable(bool enable) { |
| inquiry_scan_enable_ = enable; |
| } |
| |
| void LinkLayerController::SetPageScanEnable(bool enable) { |
| page_scan_enable_ = enable; |
| } |
| |
| void LinkLayerController::SetPageTimeout(uint16_t page_timeout) { |
| page_timeout_ = page_timeout; |
| } |
| |
| ErrorCode LinkLayerController::AddScoConnection(uint16_t connection_handle, |
| uint16_t packet_type, |
| ScoDatapath datapath) { |
| if (!connections_.HasHandle(connection_handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| Address bd_addr = connections_.GetAddress(connection_handle).GetAddress(); |
| if (connections_.HasPendingScoConnection(bd_addr)) { |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| LOG_INFO("Creating SCO connection with %s", bd_addr.ToString().c_str()); |
| |
| // Save connection parameters. |
| ScoConnectionParameters connection_parameters = { |
| 8000, |
| 8000, |
| 0xffff, |
| 0x60 /* 16bit CVSD */, |
| (uint8_t)bluetooth::hci::RetransmissionEffort::NO_RETRANSMISSION, |
| (uint16_t)((uint16_t)((packet_type >> 5) & 0x7U) | |
| (uint16_t)bluetooth::hci::SynchronousPacketTypeBits:: |
| NO_2_EV3_ALLOWED | |
| (uint16_t)bluetooth::hci::SynchronousPacketTypeBits:: |
| NO_3_EV3_ALLOWED | |
| (uint16_t)bluetooth::hci::SynchronousPacketTypeBits:: |
| NO_2_EV5_ALLOWED | |
| (uint16_t)bluetooth::hci::SynchronousPacketTypeBits:: |
| NO_3_EV5_ALLOWED)}; |
| connections_.CreateScoConnection( |
| connections_.GetAddress(connection_handle).GetAddress(), |
| connection_parameters, SCO_STATE_PENDING, datapath, true); |
| |
| // Send SCO connection request to peer. |
| SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create( |
| GetAddress(), bd_addr, connection_parameters.transmit_bandwidth, |
| connection_parameters.receive_bandwidth, |
| connection_parameters.max_latency, connection_parameters.voice_setting, |
| connection_parameters.retransmission_effort, |
| connection_parameters.packet_type, class_of_device_)); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::SetupSynchronousConnection( |
| uint16_t connection_handle, uint32_t transmit_bandwidth, |
| uint32_t receive_bandwidth, uint16_t max_latency, uint16_t voice_setting, |
| uint8_t retransmission_effort, uint16_t packet_types, |
| ScoDatapath datapath) { |
| if (!connections_.HasHandle(connection_handle)) { |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| Address bd_addr = connections_.GetAddress(connection_handle).GetAddress(); |
| if (connections_.HasPendingScoConnection(bd_addr)) { |
| // This command may be used to modify an exising eSCO link. |
| // Skip for now. TODO: should return an event |
| // HCI_Synchronous_Connection_Changed on both sides. |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| LOG_INFO("Creating eSCO connection with %s", bd_addr.ToString().c_str()); |
| |
| // Save connection parameters. |
| ScoConnectionParameters connection_parameters = { |
| transmit_bandwidth, receive_bandwidth, max_latency, |
| voice_setting, retransmission_effort, packet_types}; |
| connections_.CreateScoConnection( |
| connections_.GetAddress(connection_handle).GetAddress(), |
| connection_parameters, SCO_STATE_PENDING, datapath); |
| |
| // Send eSCO connection request to peer. |
| SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create( |
| GetAddress(), bd_addr, transmit_bandwidth, receive_bandwidth, max_latency, |
| voice_setting, retransmission_effort, packet_types, class_of_device_)); |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::AcceptSynchronousConnection( |
| Address bd_addr, uint32_t transmit_bandwidth, uint32_t receive_bandwidth, |
| uint16_t max_latency, uint16_t voice_setting, uint8_t retransmission_effort, |
| uint16_t packet_types) { |
| LOG_INFO("Accepting eSCO connection request from %s", |
| bd_addr.ToString().c_str()); |
| |
| if (!connections_.HasPendingScoConnection(bd_addr)) { |
| LOG_INFO("No pending eSCO connection for %s", bd_addr.ToString().c_str()); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| ErrorCode status = ErrorCode::SUCCESS; |
| uint16_t sco_handle = 0; |
| ScoLinkParameters link_parameters = {}; |
| ScoConnectionParameters connection_parameters = { |
| transmit_bandwidth, receive_bandwidth, max_latency, |
| voice_setting, retransmission_effort, packet_types}; |
| |
| if (!connections_.AcceptPendingScoConnection( |
| bd_addr, connection_parameters, [this, bd_addr] { |
| return LinkLayerController::StartScoStream(bd_addr); |
| })) { |
| connections_.CancelPendingScoConnection(bd_addr); |
| status = ErrorCode::STATUS_UNKNOWN; // TODO: proper status code |
| } else { |
| sco_handle = connections_.GetScoHandle(bd_addr); |
| link_parameters = connections_.GetScoLinkParameters(bd_addr); |
| } |
| |
| // Send eSCO connection response to peer. |
| SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create( |
| GetAddress(), bd_addr, (uint8_t)status, |
| link_parameters.transmission_interval, |
| link_parameters.retransmission_window, link_parameters.rx_packet_length, |
| link_parameters.tx_packet_length, link_parameters.air_mode, |
| link_parameters.extended)); |
| |
| // Schedule HCI Synchronous Connection Complete event. |
| ScheduleTask(kNoDelayMs, [this, status, sco_handle, bd_addr, |
| link_parameters]() { |
| send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create( |
| ErrorCode(status), sco_handle, bd_addr, |
| link_parameters.extended ? bluetooth::hci::ScoLinkType::ESCO |
| : bluetooth::hci::ScoLinkType::SCO, |
| link_parameters.extended ? link_parameters.transmission_interval : 0, |
| link_parameters.extended ? link_parameters.retransmission_window : 0, |
| link_parameters.extended ? link_parameters.rx_packet_length : 0, |
| link_parameters.extended ? link_parameters.tx_packet_length : 0, |
| bluetooth::hci::ScoAirMode(link_parameters.air_mode))); |
| }); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::RejectSynchronousConnection(Address bd_addr, |
| uint16_t reason) { |
| LOG_INFO("Rejecting eSCO connection request from %s", |
| bd_addr.ToString().c_str()); |
| |
| if (reason == (uint8_t)ErrorCode::SUCCESS) { |
| reason = (uint8_t)ErrorCode::REMOTE_USER_TERMINATED_CONNECTION; |
| } |
| if (!connections_.HasPendingScoConnection(bd_addr)) { |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| connections_.CancelPendingScoConnection(bd_addr); |
| |
| // Send eSCO connection response to peer. |
| SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create( |
| GetAddress(), bd_addr, reason, 0, 0, 0, 0, 0, 0)); |
| |
| // Schedule HCI Synchronous Connection Complete event. |
| ScheduleTask(kNoDelayMs, [this, reason, bd_addr]() { |
| send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create( |
| ErrorCode(reason), 0, bd_addr, bluetooth::hci::ScoLinkType::ESCO, 0, 0, |
| 0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT)); |
| }); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::CheckExpiringConnection(uint16_t handle) { |
| if (!connections_.HasHandle(handle)) { |
| return; |
| } |
| |
| if (connections_.HasLinkExpired(handle)) { |
| Disconnect(handle, ErrorCode::CONNECTION_TIMEOUT, |
| ErrorCode::CONNECTION_TIMEOUT); |
| return; |
| } |
| |
| if (connections_.IsLinkNearExpiring(handle)) { |
| AddressWithType my_address = connections_.GetOwnAddress(handle); |
| AddressWithType destination = connections_.GetAddress(handle); |
| SendLinkLayerPacket(model::packets::PingRequestBuilder::Create( |
| my_address.GetAddress(), destination.GetAddress())); |
| ScheduleTask(std::chrono::duration_cast<milliseconds>( |
| connections_.TimeUntilLinkExpired(handle)), |
| [this, handle] { CheckExpiringConnection(handle); }); |
| return; |
| } |
| |
| ScheduleTask(std::chrono::duration_cast<milliseconds>( |
| connections_.TimeUntilLinkNearExpiring(handle)), |
| [this, handle] { CheckExpiringConnection(handle); }); |
| } |
| |
| void LinkLayerController::IncomingPingRequest( |
| model::packets::LinkLayerPacketView incoming) { |
| auto view = model::packets::PingRequestView::Create(incoming); |
| ASSERT(view.IsValid()); |
| SendLinkLayerPacket(model::packets::PingResponseBuilder::Create( |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress())); |
| } |
| |
| TaskId LinkLayerController::StartScoStream(Address address) { |
| auto sco_builder = bluetooth::hci::ScoBuilder::Create( |
| connections_.GetScoHandle(address), PacketStatusFlag::CORRECTLY_RECEIVED, |
| {0, 0, 0, 0, 0}); |
| |
| auto bytes = std::make_shared<std::vector<uint8_t>>(); |
| bluetooth::packet::BitInserter bit_inserter(*bytes); |
| sco_builder->Serialize(bit_inserter); |
| auto raw_view = |
| bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian>(bytes); |
| auto sco_view = bluetooth::hci::ScoView::Create(raw_view); |
| ASSERT(sco_view.IsValid()); |
| |
| return SchedulePeriodicTask(0ms, 20ms, [this, address, sco_view]() { |
| LOG_INFO("SCO sending..."); |
| SendScoToRemote(sco_view); |
| }); |
| } |
| |
| TaskId LinkLayerController::NextTaskId() { |
| TaskId task_id = task_counter_++; |
| while ( |
| task_id == kInvalidTaskId || |
| std::any_of(task_queue_.begin(), task_queue_.end(), |
| [=](Task const& task) { return task.task_id == task_id; })) { |
| task_id = task_counter_++; |
| } |
| return task_id; |
| } |
| |
| TaskId LinkLayerController::ScheduleTask(std::chrono::milliseconds delay, |
| TaskCallback task_callback) { |
| TaskId task_id = NextTaskId(); |
| task_queue_.emplace(std::chrono::steady_clock::now() + delay, |
| std::move(task_callback), task_id); |
| return task_id; |
| } |
| |
| TaskId LinkLayerController::SchedulePeriodicTask( |
| std::chrono::milliseconds delay, std::chrono::milliseconds period, |
| TaskCallback task_callback) { |
| TaskId task_id = NextTaskId(); |
| task_queue_.emplace(std::chrono::steady_clock::now() + delay, period, |
| std::move(task_callback), task_id); |
| return task_id; |
| } |
| |
| void LinkLayerController::CancelScheduledTask(TaskId task_id) { |
| auto it = task_queue_.cbegin(); |
| for (; it != task_queue_.cend(); it++) { |
| if (it->task_id == task_id) { |
| task_queue_.erase(it); |
| return; |
| } |
| } |
| } |
| |
| void LinkLayerController::RunPendingTasks() { |
| std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); |
| while (!task_queue_.empty()) { |
| auto it = task_queue_.begin(); |
| if (it->time > now) { |
| break; |
| } |
| |
| Task task = *it; |
| task_queue_.erase(it); |
| task.callback(); |
| |
| // Re-insert periodic tasks after updating the |
| // time by the period. |
| if (task.periodic) { |
| task.time = now + task.period; |
| task_queue_.insert(task); |
| } |
| } |
| } |
| |
| } // namespace rootcanal |