| /* |
| * 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 "model/controller/link_layer_controller.h" |
| |
| #include <packet_runtime.h> |
| |
| #include <algorithm> |
| #include <array> |
| #include <chrono> |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstdlib> |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| #include <vector> |
| |
| #include "crypto/crypto.h" |
| #include "hci/address.h" |
| #include "hci/address_with_type.h" |
| #include "log.h" |
| #include "model/controller/acl_connection.h" |
| #include "model/controller/acl_connection_handler.h" |
| #include "model/controller/controller_properties.h" |
| #include "model/controller/le_advertiser.h" |
| #include "model/controller/sco_connection.h" |
| #include "packets/hci_packets.h" |
| #include "packets/link_layer_packets.h" |
| #include "phy.h" |
| #include "rust/include/rootcanal_rs.h" |
| |
| using namespace std::chrono; |
| using bluetooth::hci::Address; |
| using bluetooth::hci::AddressType; |
| using bluetooth::hci::AddressWithType; |
| using bluetooth::hci::LLFeaturesBits; |
| using bluetooth::hci::SubeventCode; |
| |
| using namespace model::packets; |
| using namespace std::literals; |
| |
| using TaskId = rootcanal::LinkLayerController::TaskId; |
| |
| namespace rootcanal { |
| |
| constexpr milliseconds kNoDelayMs(0); |
| constexpr milliseconds kPageInterval(1000); |
| |
| 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)) { |
| INFO(id_, "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()) { |
| INFO(id_, "advertising, scanning or initiating are currently active"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| if (random_address == Address::kEmpty) { |
| INFO(id_, "the random address may not be set to 00:00:00:00:00:00"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| 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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, "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) { |
| INFO(id_, "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) { |
| INFO(id_, "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) { |
| INFO(id_, "TX_PhyS {:x} configures unsupported or reserved bits", tx_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| if ((rx_phys & ~supported_phys) != 0) { |
| INFO(id_, "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) { |
| INFO(id_, "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) { |
| INFO(id_, "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) { |
| INFO(id_, "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) { |
| INFO(id_, "TX_PhyS ({:x}) configures unsupported or reserved bits", |
| tx_phys); |
| return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; |
| } |
| if ((rx_phys & ~supported_phys) != 0) { |
| INFO(id_, "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) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| incoming.GetSourceAddress()); |
| 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) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| incoming.GetSourceAddress()); |
| 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) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| incoming.GetSourceAddress()); |
| 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()) { |
| INFO(id_, |
| "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) { |
| INFO(id_, "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 && !irk_is_zero(peer_irk))) { |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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). |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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). |
| INFO(id_, |
| "peer identity address {}[{}] not found in the resolving list," |
| " or peer resolvable address unavailable", |
| peer_identity_address, PeerAddressTypeText(peer_identity_address_type)); |
| 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). |
| INFO(id_, |
| "peer identity address {}[{}] not found in the resolving list," |
| " or peer resolvable address unavailable", |
| peer_identity_address, PeerAddressTypeText(peer_identity_address_type)); |
| 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()) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, |
| "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). |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, |
| "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) { |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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. |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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) { |
| INFO(id_, "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) { |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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). |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "the controller is already synchronized on the periodic advertising" |
| " train from {}[{}] - SID=0x{:x}", |
| advertiser_address, |
| bluetooth::hci::AdvertiserAddressTypeText(advertiser_address_type), |
| 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)) { |
| INFO(id_, |
| "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)) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, "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) { |
| INFO(id_, "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()) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, |
| "legacy advertising command rejected because extended advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| if (!enable) { |
| scanner_.scan_enable = false; |
| scanner_.pending_scan_request = {}; |
| 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) { |
| INFO(id_, |
| "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_.pending_scan_request = {}; |
| 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::ScanningPhyParameters> |
| 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()) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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())) { |
| INFO(id_, |
| "scanning_phy_parameters ({})" |
| " 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) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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_) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, |
| "extended advertising command rejected because legacy advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| if (!enable) { |
| scanner_.scan_enable = false; |
| scanner_.pending_scan_request = {}; |
| 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)) { |
| INFO(id_, |
| "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) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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_.pending_scan_request = {}; |
| 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()) { |
| INFO(id_, |
| "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()) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "supervision_timeout ({} ms) is smaller that the minimal supervision " |
| "timeout allowed by connection_interval_max and max_latency ({} 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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "own_address_type is Resolvable_Or_Random_Address but the" |
| " Resolving_List does not contain a matching entry and the" |
| " Random_Address is not initialized"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| 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()) { |
| INFO(id_, "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::InitiatingPhyParameters> |
| initiating_phy_parameters) { |
| // Extended advertising commands are disallowed when legacy advertising |
| // commands were used since the last reset. |
| if (!SelectExtendedAdvertising()) { |
| INFO(id_, |
| "extended advertising command rejected because legacy advertising" |
| " is being used"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // 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()) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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())) { |
| INFO(id_, |
| "initiating_phy_parameters ({})" |
| " 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) { |
| INFO(id_, "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) { |
| INFO(id_, |
| "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_) { |
| INFO(id_, |
| "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.connection_interval_min_ < 0x6 || |
| parameter.connection_interval_min_ > 0x0c80 || |
| parameter.connection_interval_max_ < 0x6 || |
| parameter.connection_interval_max_ > 0x0c80) { |
| INFO(id_, |
| "connection_interval_min (0x{:04x}) and/or " |
| "connection_interval_max (0x{:04x}) are outside the range" |
| " of supported values (0x6 - 0x0c80)", |
| parameter.connection_interval_min_, |
| parameter.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 (parameter.connection_interval_max_ < |
| parameter.connection_interval_min_) { |
| INFO(id_, |
| "connection_interval_min (0x{:04x}) is larger than" |
| " connection_interval_max (0x{:04x})", |
| parameter.connection_interval_min_, |
| parameter.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 (parameter.max_latency_ > 0x01f3) { |
| INFO(id_, |
| "max_latency (0x{:04x}) is outside the range" |
| " of supported values (0x0 - 0x01f3)", |
| parameter.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 (parameter.supervision_timeout_ < 0xa || |
| parameter.supervision_timeout_ > 0x0c80) { |
| INFO(id_, |
| "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.max_latency_) * |
| slots(2 * parameter.connection_interval_max_) * 2); |
| if (parameter.supervision_timeout_ * 10ms < min_supervision_timeout) { |
| INFO( |
| id_, |
| "supervision_timeout ({} ms) is smaller that the minimal supervision " |
| "timeout allowed by connection_interval_max and max_latency ({} 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) { |
| INFO(id_, |
| "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) { |
| INFO(id_, |
| "own_address_type is Resolvable_Or_Random_Address but the" |
| " Resolving_List does not contain a matching entry and the" |
| " Random_Address is not initialized"); |
| return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; |
| } |
| |
| 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].connection_interval_min_, |
| .connection_interval_max = |
| initiating_phy_parameters[offset].connection_interval_max_, |
| .max_latency = initiating_phy_parameters[offset].max_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].connection_interval_min_, |
| .connection_interval_max = |
| initiating_phy_parameters[offset].connection_interval_max_, |
| .max_latency = initiating_phy_parameters[offset].max_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].connection_interval_min_, |
| .connection_interval_max = |
| initiating_phy_parameters[offset].connection_interval_max_, |
| .max_latency = initiating_phy_parameters[offset].max_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::array<uint8_t, 240> const& extended_inquiry_response) { |
| extended_inquiry_response_ = extended_inquiry_response; |
| } |
| |
| 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, |
| uint32_t id) |
| : id_(id), |
| address_(address), |
| properties_(properties), |
| lm_(nullptr, link_manager_destroy), |
| ll_(nullptr, link_layer_destroy) { |
| if (properties_.quirks.has_default_random_address) { |
| WARNING(id_, "Configuring a default random address for this controller"); |
| random_address_ = Address { 0xba, 0xdb, 0xad, 0xba, 0xdb, 0xad }; |
| } |
| |
| controller_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)); |
| }, |
| |
| .get_extended_features = |
| [](void* user, uint8_t features_page) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| return controller->GetLmpFeatures(features_page); |
| }, |
| |
| .get_le_features = |
| [](void* user) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| return controller->GetLeSupportedFeatures(); |
| }, |
| |
| .get_le_event_mask = |
| [](void* user) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| return controller->le_event_mask_; |
| }, |
| |
| .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]); |
| controller->send_event_(bluetooth::hci::EventBuilder::Create( |
| event_code, std::vector(data + 2, data + len))); |
| }, |
| |
| .send_lmp_packet = |
| [](void* user, const uint8_t(*to)[6], const uint8_t* data, |
| uintptr_t len) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| |
| Address source = controller->GetAddress(); |
| Address dest(*to); |
| |
| controller->SendLinkLayerPacket(model::packets::LmpBuilder::Create( |
| source, dest, std::vector(data, data + len))); |
| }, |
| |
| .send_llcp_packet = |
| [](void* user, uint16_t acl_connection_handle, const uint8_t* data, |
| uintptr_t len) { |
| auto controller = static_cast<LinkLayerController*>(user); |
| |
| if (!controller->connections_.HasHandle(acl_connection_handle)) { |
| ERROR( |
| "Dropping LLCP packet sent for unknown connection handle " |
| "0x{:x}", |
| acl_connection_handle); |
| return; |
| } |
| |
| AclConnection const& connection = |
| controller->connections_.GetAclConnection( |
| acl_connection_handle); |
| Address source = connection.GetOwnAddress().GetAddress(); |
| Address destination = connection.GetAddress().GetAddress(); |
| |
| controller->SendLinkLayerPacket(model::packets::LlcpBuilder::Create( |
| source, destination, std::vector(data, data + len))); |
| }}; |
| |
| lm_.reset(link_manager_create(controller_ops_)); |
| ll_.reset(link_layer_create(controller_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: |
| INFO(id_, "Dropping unhandled command 0x{:04x}", |
| static_cast<uint16_t>(opcode)); |
| return ErrorCode::UNKNOWN_HCI_COMMAND; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::SendCommandToRemoteByAddress( |
| OpCode opcode, pdl::packet::slice 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): { |
| pdl::packet::slice page_number_slice = args.subrange(5, 1); |
| uint8_t page_number = page_number_slice.read_le<uint8_t>(); |
| 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: |
| INFO(id_, "Dropping unhandled command 0x{:04x}", |
| static_cast<uint16_t>(opcode)); |
| return ErrorCode::UNKNOWN_HCI_COMMAND; |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::SendCommandToRemoteByHandle( |
| OpCode opcode, pdl::packet::slice 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::move(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) { |
| INFO(id_, "{} | Dropping packet not addressed to me {}->{} (type 0x{:x})", |
| address_, source_address, destination_address, |
| 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::LE_CONNECTED_ISOCHRONOUS_PDU: |
| IncomingLeConnectedIsochronousPdu(incoming); |
| break; |
| case model::packets::PacketType::DISCONNECT: |
| IncomingDisconnectPacket(incoming); |
| break; |
| case model::packets::PacketType::LMP: |
| IncomingLmpPacket(incoming); |
| break; |
| case model::packets::PacketType::LLCP: |
| IncomingLlcpPacket(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 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: |
| WARNING(id_, "Dropping unhandled packet of type {}", |
| model::packets::PacketTypeText(incoming.GetType())); |
| } |
| } |
| |
| 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; |
| } |
| |
| INFO(id_, "Acl Packet [{}] {} -> {}", acl_data.size(), |
| incoming.GetSourceAddress(), incoming.GetDestinationAddress()); |
| |
| uint16_t connection_handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| if (connection_handle == kReservedHandle) { |
| INFO(id_, "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::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)) { |
| INFO(id_, "Spurious SCO packet from {}", source); |
| 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()); |
| |
| INFO(id_, "Sco Packet [{}] {} -> {}", static_cast<int>(sco_data_bytes.size()), |
| incoming.GetSourceAddress(), incoming.GetDestinationAddress()); |
| |
| 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) { |
| INFO(id_, "Discarding response from a disconnected device {}", source); |
| 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) { |
| INFO(id_, "Discarding response from a disconnected device {}", source); |
| 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) { |
| INFO(id_, "Discarding response from a disconnected device {}", source); |
| 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) { |
| INFO(id_, "Discarding response from a disconnected device {}", source); |
| 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) { |
| INFO(id_, "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) { |
| INFO(id_, "Discarding disconnect from a disconnected device {}", peer); |
| 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 0x{:x}", 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()))); |
| } else { |
| ASSERT(link_layer_remove_link(ll_.get(), handle)); |
| } |
| } |
| |
| 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: |
| WARNING(id_, "Unhandled Incoming Inquiry of type {}", |
| 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::ExtendedInquiryResultBuilder::Create( |
| inquiry_response.GetSourceAddress(), |
| static_cast<bluetooth::hci::PageScanRepetitionMode>( |
| inquiry_response.GetPageScanRepetitionMode()), |
| inquiry_response.GetClassOfDevice(), |
| inquiry_response.GetClockOffset(), inquiry_response.GetRssi(), |
| inquiry_response.GetExtendedInquiryResponse())); |
| } break; |
| |
| default: |
| WARNING(id_, "Unhandled Incoming Inquiry Response of type {}", |
| static_cast<int>(basic_inquiry_response.GetInquiryType())); |
| } |
| } |
| |
| 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]; |
| INFO("RPA {}", rpa); |
| return rpa; |
| } |
| |
| bool LinkLayerController::irk_is_zero(std::array<uint8_t, LinkLayerController::kIrkSize> irk) { |
| return std::all_of(irk.begin(), irk.end(), [](uint8_t b) { return b == 0; }); |
| } |
| |
| // 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) { |
| DEBUG(id_, "Resolved the advertising address {} to {}", advertising_address, |
| resolved_advertising_address); |
| } |
| |
| // 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)) { |
| DEBUG(id_, |
| "Legacy advertising ignored by scanner because the advertising " |
| "address {} is not in the filter accept list", |
| resolved_advertising_address); |
| 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)) { |
| DEBUG(id_, |
| "Legacy advertising ignored by scanner because the directed " |
| "address {} does not match the current device or cannot be " |
| "resolved", |
| target_address); |
| 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()) { |
| DEBUG(id_, |
| "Legacy advertising ignored by scanner because the directed " |
| "address {} does not match the current device or is not a " |
| "resovable private address", |
| target_address); |
| 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.bytes())) { |
| should_send_advertising_report = false; |
| } else { |
| scanner_.AddPacketToHistory(pdu.bytes()); |
| } |
| } |
| |
| // 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::LeAdvertisingResponse 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::LeAdvertisingReportBuilder::Create({response})); |
| } |
| |
| // Extended scanning. |
| if (ExtendedAdvertising() && should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) { |
| bluetooth::hci::LeExtendedAdvertisingResponse 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::LeExtendedAdvertisingReportBuilder::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) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "it is not scannable", |
| advertising_address); |
| } else if (!active_scanning) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "the scanner is passive", |
| advertising_address); |
| } else if (scanner_.pending_scan_request) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "an LE Scan request is already pending", |
| advertising_address); |
| } else if (!should_send_advertising_report) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "the advertising message was filtered", |
| advertising_address); |
| } 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; |
| |
| INFO(id_, |
| "Sending LE Scan request to advertising address {} with scanning " |
| "address {}", |
| advertising_address, scanning_address); |
| |
| // 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) { |
| DEBUG(id_, |
| "Legacy advertising ignored by initiator because it is not " |
| "connectable"); |
| return; |
| } |
| if (initiator_.pending_connect_request) { |
| DEBUG(id_, |
| "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) { |
| DEBUG(id_, |
| "Legacy advertising ignored by initiator because the " |
| "advertising address {} does not match the peer address {}", |
| resolved_advertising_address, initiator_.peer_address); |
| return; |
| } |
| break; |
| case bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST: |
| if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) { |
| DEBUG(id_, |
| "Legacy advertising ignored by initiator because the " |
| "advertising address {} is not in the filter accept list", |
| resolved_advertising_address); |
| 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)) { |
| DEBUG(id_, |
| "Directed legacy advertising ignored by initiator because the " |
| "target address {} does not match the current device addresses", |
| resolved_advertising_address); |
| 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)) { |
| DEBUG(id_, |
| "Directed legacy advertising ignored by initiator because the " |
| "target address {} is static or public and the initiator is " |
| "configured to use resolvable addresses", |
| resolved_advertising_address); |
| 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)) { |
| WARNING(id_, "CreatePendingLeConnection failed for connection to {}", |
| advertising_address); |
| } |
| |
| initiator_.pending_connect_request = advertising_address; |
| initiator_.initiating_address = initiating_address.GetAddress(); |
| |
| INFO(id_, "Sending LE Connect request to {} with initiating address {}", |
| resolved_advertising_address, initiating_address); |
| |
| // 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()) { |
| DEBUG(id_, "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) { |
| DEBUG(id_, "Resolved the advertising address {} to {}", advertising_address, |
| advertising_address.GetAddressType(), resolved_advertising_address, |
| 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)) { |
| DEBUG(id_, |
| "Extended advertising ignored by scanner because the advertising " |
| "address {} is not in the filter accept list", |
| resolved_advertising_address); |
| 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)) { |
| DEBUG(id_, |
| "Extended advertising ignored by scanner because the directed " |
| "address {} does not match the current device or cannot be " |
| "resolved", |
| target_address); |
| 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()) { |
| DEBUG(id_, |
| "Extended advertising ignored by scanner because the directed " |
| "address {} does not match the current device or is not a " |
| "resovable private address", |
| target_address); |
| return; |
| } |
| break; |
| } |
| } |
| |
| bool should_send_advertising_report = true; |
| if (scanner_.filter_duplicates != |
| bluetooth::hci::FilterDuplicates::DISABLED) { |
| if (scanner_.IsPacketInHistory(pdu.bytes())) { |
| should_send_advertising_report = false; |
| } else { |
| scanner_.AddPacketToHistory(pdu.bytes()); |
| } |
| } |
| |
| if (should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) { |
| bluetooth::hci::LeExtendedAdvertisingResponse 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::LeExtendedAdvertisingReportBuilder::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) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "it is not scannable", |
| advertising_address); |
| } else if (!active_scanning) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "the scanner is passive", |
| advertising_address); |
| } else if (scanner_.pending_scan_request) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "an LE Scan request is already pending", |
| advertising_address); |
| } else if (!should_send_advertising_report) { |
| DEBUG(id_, |
| "Not sending LE Scan request to advertising address {} because " |
| "the advertising message was filtered", |
| advertising_address); |
| } 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; |
| |
| INFO(id_, |
| "Sending LE Scan request to advertising address {} with scanning " |
| "address {}", |
| advertising_address, scanning_address); |
| |
| // 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()) { |
| DEBUG(id_, "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()) { |
| DEBUG(id_, |
| "Extended advertising ignored by initiator because it is not " |
| "connectable"); |
| return; |
| } |
| if (initiator_.pending_connect_request) { |
| DEBUG( |
| id_, |
| "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) { |
| DEBUG(id_, |
| "Extended advertising ignored by initiator because the " |
| "advertising address {} does not match the peer address {}", |
| resolved_advertising_address, initiator_.peer_address); |
| return; |
| } |
| break; |
| case bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST: |
| if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) { |
| DEBUG(id_, |
| "Extended advertising ignored by initiator because the " |
| "advertising address {} is not in the filter accept list", |
| resolved_advertising_address); |
| 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)) { |
| DEBUG(id_, |
| "Directed extended advertising ignored by initiator because the " |
| "target address {} does not match the current device addresses", |
| resolved_advertising_address); |
| 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)) { |
| DEBUG(id_, |
| "Directed extended advertising ignored by initiator because the " |
| "target address {} is static or public and the initiator is " |
| "configured to use resolvable addresses", |
| resolved_advertising_address); |
| 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)) { |
| WARNING(id_, "CreatePendingLeConnection failed for connection to {}", |
| advertising_address); |
| } |
| |
| initiator_.pending_connect_request = advertising_address; |
| initiator_.initiating_address = initiating_address.GetAddress(); |
| |
| INFO(id_, "Sending LE Connect request to {} with initiating address {}", |
| resolved_advertising_address, initiating_address); |
| |
| // 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()) { |
| DEBUG(id_, "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) { |
| INFO(id_, "Established Sync with advertiser {}[{}] - SID 0x{:x}", |
| advertiser_address, |
| bluetooth::hci::AdvertiserAddressTypeText(advertiser_address_type), |
| 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()); |
| |
| INFO(id_, "Received eSCO connection request from {}", address); |
| |
| // Automatically reject if connection request was already sent |
| // from the current device. |
| if (connections_.HasPendingScoConnection(address)) { |
| INFO(id_, |
| "Rejecting eSCO connection request from {}, " |
| "an eSCO connection already exist with this device", |
| address); |
| |
| 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); |
| |
| INFO(id_, "Received eSCO connection response with status 0x{:02x} from {}", |
| static_cast<unsigned>(status), incoming.GetSourceAddress()); |
| |
| 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); |
| |
| INFO(id_, |
| "Received eSCO disconnection request with" |
| " reason 0x{:02x} from {}", |
| static_cast<unsigned>(reason), incoming.GetSourceAddress()); |
| |
| 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())); |
| } |
| |
| void LinkLayerController::IncomingLlcpPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| Address address = incoming.GetSourceAddress(); |
| auto request = model::packets::LlcpView::Create(incoming); |
| ASSERT(request.IsValid()); |
| auto payload = request.GetPayload(); |
| auto packet = std::vector(payload.begin(), payload.end()); |
| uint16_t acl_connection_handle = connections_.GetHandleOnlyAddress(address); |
| |
| if (acl_connection_handle == kReservedHandle) { |
| INFO(id_, "Dropping LLCP packet since connection does not exist"); |
| return; |
| } |
| |
| ASSERT(link_layer_ingest_llcp(ll_.get(), acl_connection_handle, packet.data(), |
| packet.size())); |
| } |
| |
| void LinkLayerController::IncomingLeConnectedIsochronousPdu( |
| LinkLayerPacketView incoming) { |
| auto pdu = model::packets::LeConnectedIsochronousPduView::Create(incoming); |
| ASSERT(pdu.IsValid()); |
| auto data = pdu.GetData(); |
| auto packet = std::vector(data.begin(), data.end()); |
| uint8_t cig_id = pdu.GetCigId(); |
| uint8_t cis_id = pdu.GetCisId(); |
| uint16_t cis_connection_handle = 0; |
| uint16_t iso_sdu_length = packet.size(); |
| |
| if (!link_layer_get_cis_connection_handle(ll_.get(), cig_id, cis_id, |
| &cis_connection_handle)) { |
| INFO(id_, |
| "Dropping CIS pdu received on disconnected CIS cig_id={}, cis_id={}", |
| cig_id, cis_id); |
| return; |
| } |
| |
| // Fragment the ISO SDU if larger than the maximum payload size (4095). |
| constexpr size_t kMaxPayloadSize = 4095 - 4; // remove sequence_number and |
| // iso_sdu_length |
| size_t remaining_size = packet.size(); |
| size_t offset = 0; |
| auto packet_boundary_flag = |
| remaining_size <= kMaxPayloadSize |
| ? bluetooth::hci::IsoPacketBoundaryFlag::COMPLETE_SDU |
| : bluetooth::hci::IsoPacketBoundaryFlag::FIRST_FRAGMENT; |
| |
| do { |
| size_t fragment_size = std::min(kMaxPayloadSize, remaining_size); |
| std::vector<uint8_t> fragment(packet.data() + offset, |
| packet.data() + offset + fragment_size); |
| |
| send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create( |
| cis_connection_handle, packet_boundary_flag, pdu.GetSequenceNumber(), |
| iso_sdu_length, bluetooth::hci::IsoPacketStatusFlag::VALID, |
| std::move(fragment))); |
| |
| remaining_size -= fragment_size; |
| offset += fragment_size; |
| packet_boundary_flag = |
| remaining_size <= kMaxPayloadSize |
| ? bluetooth::hci::IsoPacketBoundaryFlag::LAST_FRAGMENT |
| : bluetooth::hci::IsoPacketBoundaryFlag::CONTINUATION_FRAGMENT; |
| } while (remaining_size > 0); |
| } |
| |
| void LinkLayerController::HandleIso(bluetooth::hci::IsoView iso) { |
| uint16_t cis_connection_handle = iso.GetConnectionHandle(); |
| auto pb_flag = iso.GetPbFlag(); |
| auto ts_flag = iso.GetTsFlag(); |
| auto iso_data_load = iso.GetPayload(); |
| |
| // In the Host to Controller direction, ISO_Data_Load_Length |
| // shall be less than or equal to the size of the buffer supported by the |
| // Controller (which is returned using the ISO_Data_Packet_Length return |
| // parameter of the LE Read Buffer Size command). |
| if (iso_data_load.size() > properties_.iso_data_packet_length) { |
| FATAL(id_, |
| "Received ISO HCI packet with ISO_Data_Load_Length ({}) larger than" |
| " the controller buffer size ISO_Data_Packet_Length ({})", |
| iso_data_load.size(), properties_.iso_data_packet_length); |
| } |
| |
| // The TS_Flag bit shall only be set if the PB_Flag field equals 0b00 or 0b10. |
| if (ts_flag == bluetooth::hci::TimeStampFlag::PRESENT && |
| (pb_flag == |
| bluetooth::hci::IsoPacketBoundaryFlag::CONTINUATION_FRAGMENT || |
| pb_flag == bluetooth::hci::IsoPacketBoundaryFlag::LAST_FRAGMENT)) { |
| FATAL(id_, |
| "Received ISO HCI packet with TS_Flag set, but no ISO Header is " |
| "expected"); |
| } |
| |
| uint8_t cig_id = 0; |
| uint8_t cis_id = 0; |
| uint16_t acl_connection_handle = kReservedHandle; |
| uint16_t packet_sequence_number = 0; |
| uint16_t max_sdu_length = 0; |
| |
| if (!link_layer_get_cis_information(ll_.get(), cis_connection_handle, |
| &acl_connection_handle, &cig_id, &cis_id, |
| &max_sdu_length)) { |
| INFO(id_, "Ignoring CIS pdu received on disconnected CIS handle={}", |
| cis_connection_handle); |
| return; |
| } |
| |
| if (pb_flag == bluetooth::hci::IsoPacketBoundaryFlag::FIRST_FRAGMENT || |
| pb_flag == bluetooth::hci::IsoPacketBoundaryFlag::COMPLETE_SDU) { |
| iso_sdu_.clear(); |
| } |
| |
| switch (ts_flag) { |
| case bluetooth::hci::TimeStampFlag::PRESENT: { |
| auto iso_with_timestamp = |
| bluetooth::hci::IsoWithTimestampView::Create(iso); |
| ASSERT(iso_with_timestamp.IsValid()); |
| auto iso_payload = iso_with_timestamp.GetPayload(); |
| iso_sdu_.insert(iso_sdu_.end(), iso_payload.begin(), iso_payload.end()); |
| packet_sequence_number = iso_with_timestamp.GetPacketSequenceNumber(); |
| break; |
| } |
| default: |
| case bluetooth::hci::TimeStampFlag::NOT_PRESENT: { |
| auto iso_without_timestamp = |
| bluetooth::hci::IsoWithoutTimestampView::Create(iso); |
| ASSERT(iso_without_timestamp.IsValid()); |
| auto iso_payload = iso_without_timestamp.GetPayload(); |
| iso_sdu_.insert(iso_sdu_.end(), iso_payload.begin(), iso_payload.end()); |
| packet_sequence_number = iso_without_timestamp.GetPacketSequenceNumber(); |
| break; |
| } |
| } |
| |
| if (pb_flag == bluetooth::hci::IsoPacketBoundaryFlag::LAST_FRAGMENT || |
| pb_flag == bluetooth::hci::IsoPacketBoundaryFlag::COMPLETE_SDU) { |
| // Validate that the Host stack is not sending ISO SDUs that are larger |
| // that what was configured for the CIS. |
| if (iso_sdu_.size() > max_sdu_length) { |
| WARNING( |
| id_, |
| "attempted to send an SDU of length {} that exceeds the configure " |
| "Max_SDU_Length ({})", |
| iso_sdu_.size(), max_sdu_length); |
| return; |
| } |
| |
| SendLeLinkLayerPacket( |
| model::packets::LeConnectedIsochronousPduBuilder::Create( |
| address_, |
| connections_.GetAddress(acl_connection_handle).GetAddress(), cig_id, |
| cis_id, packet_sequence_number, std::move(iso_sdu_))); |
| } |
| } |
| |
| 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) { |
| WARNING(id_, "No pending connection for connection from {}", address); |
| 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 { |
| WARNING(id_, "Unhandled resolved address type {} -> {}", address, |
| peer_resolved_address); |
| } |
| 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))); |
| } |
| |
| // Update the link layer with the new link. |
| ASSERT(link_layer_add_link( |
| ll_.get(), handle, |
| reinterpret_cast<const uint8_t(*)[6]>(address.GetAddress().data()), |
| static_cast<uint8_t>(role))); |
| |
| // 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()) { |
| DEBUG(id_, |
| "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) { |
| DEBUG(id_, |
| "LE Connect request ignored by legacy advertiser because the " |
| "advertising address {} does not match {}", |
| advertising_address, legacy_advertiser_.GetAdvertisingAddress()); |
| 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) { |
| DEBUG(id_, "Resolved the initiating address {} to {}", initiating_address, |
| resolved_initiating_address); |
| } |
| |
| // 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) { |
| DEBUG(id_, |
| "LE Connect request ignored by legacy advertiser because the " |
| "initiating address {} does not match the target address {}", |
| resolved_initiating_address, legacy_advertiser_.GetTargetAddress()); |
| 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)) { |
| DEBUG(id_, |
| "LE Connect request ignored by legacy advertiser because the " |
| "initiating address {} is not in the filter accept list", |
| resolved_initiating_address); |
| return false; |
| } |
| break; |
| } |
| } |
| |
| INFO(id_, |
| "Accepting LE Connect request to legacy advertiser from initiating " |
| "address {}", |
| resolved_initiating_address); |
| |
| if (!connections_.CreatePendingLeConnection( |
| initiating_address, |
| resolved_initiating_address != initiating_address |
| ? resolved_initiating_address |
| : AddressWithType{}, |
| advertising_address)) { |
| WARNING(id_, "CreatePendingLeConnection failed for connection from {}", |
| initiating_address.GetAddress()); |
| 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()) { |
| DEBUG(id_, |
| "LE Connect request ignored by extended advertiser {} 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) { |
| DEBUG(id_, |
| "LE Connect request ignored by extended advertiser {} because the " |
| "advertising address {} does not match {}", |
| advertiser.advertising_handle, advertising_address, |
| advertiser.GetAdvertisingAddress()); |
| 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) { |
| DEBUG(id_, "Resolved the initiating address {} to {}", initiating_address, |
| resolved_initiating_address); |
| } |
| |
| // 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) { |
| DEBUG(id_, |
| "LE Connect request ignored by extended advertiser {} because the " |
| "initiating address {} does not match the target address {}", |
| advertiser.advertising_handle, resolved_initiating_address, |
| advertiser.GetTargetAddress()); |
| 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)) { |
| DEBUG(id_, |
| "LE Connect request ignored by extended advertiser {} because " |
| "the initiating address {} is not in the filter accept list", |
| advertiser.advertising_handle, resolved_initiating_address); |
| return false; |
| } |
| break; |
| } |
| } |
| |
| INFO(id_, |
| "Accepting LE Connect request to extended advertiser {} from initiating " |
| "address {}", |
| advertiser.advertising_handle, resolved_initiating_address); |
| |
| if (!connections_.CreatePendingLeConnection( |
| initiating_address, |
| resolved_initiating_address != initiating_address |
| ? resolved_initiating_address |
| : AddressWithType{}, |
| advertising_address)) { |
| WARNING(id_, "CreatePendingLeConnection failed for connection from {}", |
| initiating_address.GetAddress()); |
| 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())}; |
| |
| INFO(id_, "Received LE Connect complete response with advertising address {}", |
| advertising_address); |
| |
| 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) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| peer); |
| 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) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| peer); |
| 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) { |
| INFO(id_, "IncomingLeEncryptConnection"); |
| |
| Address peer = incoming.GetSourceAddress(); |
| uint16_t handle = connections_.GetHandleOnlyAddress(peer); |
| if (handle == kReservedHandle) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| peer); |
| 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) { |
| INFO(id_, "IncomingLeEncryptConnectionResponse"); |
| // TODO: Check keys |
| uint16_t handle = |
| connections_.GetHandleOnlyAddress(incoming.GetSourceAddress()); |
| if (handle == kReservedHandle) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| incoming.GetSourceAddress()); |
| 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) { |
| WARNING(id_, "@{}: Unknown connection @{}", |
| incoming.GetDestinationAddress(), incoming.GetSourceAddress()); |
| } |
| 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) { |
| INFO(id_, "@{}: Unknown connection @{}", incoming.GetDestinationAddress(), |
| incoming.GetSourceAddress()); |
| 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()) { |
| DEBUG(id_, |
| "LE Scan request ignored by legacy advertiser because it is not " |
| "scannable"); |
| return; |
| } |
| |
| if (advertising_address != legacy_advertiser_.advertising_address) { |
| DEBUG( |
| id_, |
| "LE Scan request ignored by legacy advertiser because the advertising " |
| "address {} does not match {}", |
| advertising_address, legacy_advertiser_.GetAdvertisingAddress()); |
| 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)) { |
| DEBUG( |
| id_, |
| "LE Scan request ignored by legacy advertiser because the scanning " |
| "address {} is not in the filter accept list", |
| resolved_scanning_address); |
| return; |
| } |
| break; |
| } |
| |
| INFO(id_, |
| "Accepting LE Scan request to legacy advertiser from scanning address " |
| "{}", |
| resolved_scanning_address); |
| |
| // 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()) { |
| DEBUG(id_, |
| "LE Scan request ignored by extended advertiser {} because it is not " |
| "scannable", |
| advertiser.advertising_handle); |
| return; |
| } |
| |
| if (advertising_address != advertiser.advertising_address) { |
| DEBUG(id_, |
| "LE Scan request ignored by extended advertiser {} because the " |
| "advertising address {} does not match {}", |
| advertiser.advertising_handle, advertising_address, |
| advertiser.GetAdvertisingAddress()); |
| 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)) { |
| DEBUG(id_, |
| "LE Scan request ignored by extended advertiser {} because the " |
| "scanning address {} is not in the filter accept list", |
| advertiser.advertising_handle, resolved_scanning_address); |
| 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) { |
| DEBUG(id_, |
| "LE Scan request ignored by extended advertiser {} because the " |
| "scanning address {} does not match the target address {}", |
| advertiser.advertising_handle, resolved_scanning_address, |
| advertiser.GetTargetAddress()); |
| return; |
| } |
| |
| INFO(id_, |
| "Accepting LE Scan request to extended advertiser {} from scanning " |
| "address {}", |
| advertiser.advertising_handle, resolved_scanning_address); |
| |
| // 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) { |
| DEBUG(id_, "Resolved the scanning address {} to {}", scanning_address, |
| resolved_scanning_address); |
| } |
| |
| 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) { |
| DEBUG(id_, |
| "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) { |
| DEBUG(id_, |
| "LE Scan response ignored by scanner because the advertising address " |
| "{} does not match the pending request {}", |
| advertising_address, scanner_.pending_scan_request.value()); |
| return; |
| } |
| |
| AddressWithType resolved_advertising_address = |
| ResolvePrivateAddress(advertising_address, IrkSelection::Peer) |
| .value_or(advertising_address); |
| |
| if (advertising_address != resolved_advertising_address) { |
| DEBUG(id_, "Resolved the advertising address {} to {}", advertising_address, |
| resolved_advertising_address); |
| } |
| |
| INFO(id_, "Accepting LE Scan response from advertising address {}", |
| resolved_advertising_address); |
| |
| scanner_.pending_scan_request = {}; |
| |
| bool should_send_advertising_report = true; |
| if (scanner_.filter_duplicates != |
| bluetooth::hci::FilterDuplicates::DISABLED) { |
| if (scanner_.IsPacketInHistory(incoming.bytes())) { |
| should_send_advertising_report = false; |
| } else { |
| scanner_.AddPacketToHistory(incoming.bytes()); |
| } |
| } |
| |
| if (LegacyAdvertising() && should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::ADVERTISING_REPORT)) { |
| bluetooth::hci::LeAdvertisingResponse 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::LeAdvertisingReportBuilder::Create({response})); |
| } |
| |
| if (ExtendedAdvertising() && should_send_advertising_report && |
| IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) { |
| bluetooth::hci::LeExtendedAdvertisingResponse 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::LeExtendedAdvertisingReportBuilder::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. |
| INFO(id_, "Extended Scan Timeout"); |
| scanner_.scan_enable = false; |
| scanner_.pending_scan_request = {}; |
| 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()) { |
| INFO(id_, "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 bd_addr = incoming.GetSourceAddress(); |
| auto page = model::packets::PageView::Create(incoming); |
| ASSERT(page.IsValid()); |
| |
| // Cannot establish two BR-EDR connections with the same peer. |
| if (connections_.GetAclConnectionHandle(bd_addr).has_value()) { |
| return; |
| } |
| |
| bool allow_role_switch = page.GetAllowRoleSwitch(); |
| if (!connections_.CreatePendingConnection( |
| bd_addr, authentication_enable_ == AuthenticationEnable::REQUIRED, |
| allow_role_switch)) { |
| // Will be triggered when multiple hosts are paging simultaneously; |
| // only one connection will be accepted. |
| WARNING(id_, "Failed to create a pending connection for {}", bd_addr); |
| return; |
| } |
| |
| if (IsEventUnmasked(EventCode::CONNECTION_REQUEST)) { |
| send_event_(bluetooth::hci::ConnectionRequestBuilder::Create( |
| bd_addr, page.GetClassOfDevice(), |
| bluetooth::hci::ConnectionRequestLinkType::ACL)); |
| } |
| } |
| |
| void LinkLayerController::IncomingPageRejectPacket( |
| model::packets::LinkLayerPacketView incoming) { |
| auto bd_addr = incoming.GetSourceAddress(); |
| auto reject = model::packets::PageRejectView::Create(incoming); |
| ASSERT(reject.IsValid()); |
| |
| if (!page_.has_value() || page_->bd_addr != bd_addr) { |
| INFO(id_, |
| "ignoring Page Reject packet received when not in Page state," |
| " or paging to a different address"); |
| return; |
| } |
| |
| INFO(id_, "Received Page Reject packet from {}", bd_addr); |
| page_ = {}; |
| |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| static_cast<ErrorCode>(reject.GetReason()), 0, bd_addr, |
| bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); |
| } |
| } |
| |
| void LinkLayerController::IncomingPageResponsePacket( |
| model::packets::LinkLayerPacketView incoming) { |
| auto bd_addr = incoming.GetSourceAddress(); |
| auto response = model::packets::PageResponseView::Create(incoming); |
| ASSERT(response.IsValid()); |
| |
| if (!page_.has_value() || page_->bd_addr != bd_addr) { |
| INFO(id_, |
| "ignoring Page Response packet received when not in Page state," |
| " or paging to a different address"); |
| return; |
| } |
| |
| INFO(id_, "Received Page Response packet from {}", bd_addr); |
| |
| uint16_t connection_handle = |
| connections_.CreateConnection(bd_addr, GetAddress(), false); |
| ASSERT(connection_handle != kReservedHandle); |
| |
| bluetooth::hci::Role role = |
| page_->allow_role_switch && response.GetTryRoleSwitch() |
| ? bluetooth::hci::Role::PERIPHERAL |
| : bluetooth::hci::Role::CENTRAL; |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| CheckExpiringConnection(connection_handle); |
| connection.SetLinkPolicySettings(default_link_policy_settings_); |
| connection.SetRole(role); |
| page_ = {}; |
| |
| ASSERT(link_manager_add_link( |
| lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); |
| |
| // Role change event before connection complete generates an HCI Role Change |
| // event on the initiator side if accepted; the event is sent before the |
| // HCI Connection Complete event. |
| if (role == bluetooth::hci::Role::PERIPHERAL && |
| IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, |
| bd_addr, role)); |
| } |
| |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connection_handle, bd_addr, |
| bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); |
| } |
| } |
| |
| void LinkLayerController::Tick() { |
| RunPendingTasks(); |
| Paging(); |
| |
| 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 = command.bytes().bytes(); |
| ASSERT(link_manager_ingest_hci(lm_.get(), packet.data(), packet.size())); |
| } |
| |
| void LinkLayerController::ForwardToLl(bluetooth::hci::CommandView command) { |
| auto packet = command.bytes().bytes(); |
| ASSERT(link_layer_ingest_hci(ll_.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)) { |
| INFO(id_, "Accepting connection request from {}", bd_addr); |
| ScheduleTask(kNoDelayMs, [this, bd_addr, try_role_switch]() { |
| INFO(id_, "Accepted connection from {}", bd_addr); |
| 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. |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| 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; |
| } |
| |
| INFO(id_, "No pending connection for {}", bd_addr); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, |
| bool try_role_switch) { |
| uint16_t connection_handle = |
| connections_.CreateConnection(bd_addr, GetAddress()); |
| if (connection_handle == kReservedHandle) { |
| INFO(id_, "CreateConnection failed"); |
| return; |
| } |
| |
| bluetooth::hci::Role role = |
| try_role_switch && connections_.IsRoleSwitchAllowedForPendingConnection() |
| ? bluetooth::hci::Role::CENTRAL |
| : bluetooth::hci::Role::PERIPHERAL; |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| CheckExpiringConnection(connection_handle); |
| connection.SetLinkPolicySettings(default_link_policy_settings_); |
| connection.SetRole(role); |
| |
| ASSERT(link_manager_add_link( |
| lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); |
| |
| // Role change event before connection complete generates an HCI Role Change |
| // event on the acceptor side if accepted; the event is sent before the |
| // HCI Connection Complete event. |
| if (role == bluetooth::hci::Role::CENTRAL && |
| IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| INFO(id_, "Role at connection setup accepted"); |
| send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, |
| bd_addr, role)); |
| } |
| |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connection_handle, bd_addr, |
| bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); |
| } |
| |
| // If the current Host was initiating a connection to the same bd_addr, |
| // send a connection complete event for the pending Create Connection |
| // command and cancel the paging. |
| if (page_.has_value() && page_->bd_addr == bd_addr) { |
| // TODO: the core specification is very unclear as to what behavior |
| // is expected when two connections are established simultaneously. |
| // This implementation considers that an HCI Connection Complete |
| // event is expected for both the HCI Create Connection and HCI Accept |
| // Connection Request commands. Both events are sent with the status |
| // for success. |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::SUCCESS, connection_handle, bd_addr, |
| bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); |
| } |
| page_ = {}; |
| } |
| |
| INFO(id_, "Sending page response to {}", bd_addr.ToString()); |
| SendLinkLayerPacket(model::packets::PageResponseBuilder::Create( |
| GetAddress(), bd_addr, try_role_switch)); |
| } |
| |
| ErrorCode LinkLayerController::RejectConnectionRequest(const Address& addr, |
| uint8_t reason) { |
| if (!connections_.HasPendingConnection(addr)) { |
| INFO(id_, "No pending connection for {}", addr); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| ScheduleTask(kNoDelayMs, [this, addr, reason]() { |
| RejectPeripheralConnection(addr, reason); |
| }); |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::RejectPeripheralConnection(const Address& addr, |
| uint8_t reason) { |
| INFO(id_, "Sending page reject to {} (reason 0x{:02x})", addr, 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& bd_addr, |
| uint16_t /* packet_type */, |
| uint8_t /* page_scan_mode */, |
| uint16_t /* clock_offset */, |
| uint8_t allow_role_switch) { |
| // RootCanal only accepts one pending outgoing connection at any time. |
| if (page_.has_value()) { |
| INFO(id_, "Create Connection command is already pending"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // Reject the command if a connection or pending connection already exists |
| // for the selected peer address. |
| if (connections_.HasPendingConnection(bd_addr) || |
| connections_.GetAclConnectionHandle(bd_addr).has_value()) { |
| INFO(id_, "Connection with {} already exists", bd_addr.ToString()); |
| return ErrorCode::CONNECTION_ALREADY_EXISTS; |
| } |
| |
| auto now = std::chrono::steady_clock::now(); |
| page_ = Page{ |
| .bd_addr = bd_addr, |
| .allow_role_switch = allow_role_switch, |
| .next_page_event = now + kPageInterval, |
| .page_timeout = now + slots(page_timeout_), |
| }; |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| ErrorCode LinkLayerController::CreateConnectionCancel(const Address& bd_addr) { |
| // If the HCI_Create_Connection_Cancel command is sent to the Controller |
| // without a preceding HCI_Create_Connection command to the same device, |
| // the BR/EDR Controller shall return an HCI_Command_Complete event with |
| // the error code Unknown Connection Identifier (0x02) |
| if (!page_.has_value() || page_->bd_addr != bd_addr) { |
| INFO(id_, "no pending connection to {}", bd_addr.ToString()); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| // The HCI_Connection_Complete event for the corresponding HCI_Create_- |
| // Connection command shall always be sent. The HCI_Connection_Complete |
| // event shall be sent after the HCI_Command_Complete event for the |
| // HCI_Create_Connection_Cancel command. If the cancellation was successful, |
| // the HCI_Connection_Complete event will be generated with the error code |
| // Unknown Connection Identifier (0x02). |
| if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { |
| ScheduleTask(kNoDelayMs, [this, bd_addr]() { |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::UNKNOWN_CONNECTION, 0, bd_addr, |
| bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); |
| }); |
| } |
| |
| page_ = {}; |
| 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); |
| INFO(id_, "Disconnecting eSCO connection with {}", remote); |
| |
| 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) { |
| INFO(id_, "Disconnecting ACL connection with {}", remote); |
| |
| 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 { |
| INFO(id_, "Disconnecting LE connection with {}", remote); |
| |
| 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()))); |
| } else { |
| ASSERT(link_layer_remove_link(ll_.get(), handle)); |
| } |
| 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 bd_addr, |
| bluetooth::hci::Role role) { |
| // The BD_ADDR command parameter indicates for which connection |
| // the role switch is to be performed and shall specify a BR/EDR Controller |
| // for which a connection already exists. |
| uint16_t connection_handle = connections_.GetHandleOnlyAddress(bd_addr); |
| if (connection_handle == kReservedHandle) { |
| INFO(id_, "unknown connection address {}", bd_addr); |
| return ErrorCode::UNKNOWN_CONNECTION; |
| } |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| |
| // If there is an (e)SCO connection between the local device and the device |
| // identified by the BD_ADDR parameter, an attempt to perform a role switch |
| // shall be rejected by the local device. |
| if (connections_.GetScoHandle(bd_addr) != kReservedHandle) { |
| INFO(id_, |
| "role switch rejected because an Sco link is opened with" |
| " the target device"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| // If the connection between the local device and the device identified by the |
| // BD_ADDR parameter is placed in Sniff mode, an attempt to perform a role |
| // switch shall be rejected by the local device. |
| if (connection.GetMode() == AclConnectionState::kSniffMode) { |
| INFO(id_, |
| "role switch rejected because the acl connection is in sniff mode"); |
| return ErrorCode::COMMAND_DISALLOWED; |
| } |
| |
| if (role != connection.GetRole()) { |
| SendLinkLayerPacket(model::packets::RoleSwitchRequestBuilder::Create( |
| GetAddress(), bd_addr)); |
| } else if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| // Note: the status is Success only if the role change procedure was |
| // actually performed, otherwise the status is >0. |
| ScheduleTask(kNoDelayMs, [this, bd_addr, role]() { |
| send_event_(bluetooth::hci::RoleChangeBuilder::Create( |
| ErrorCode::ROLE_SWITCH_FAILED, bd_addr, role)); |
| }); |
| } |
| |
| return ErrorCode::SUCCESS; |
| } |
| |
| void LinkLayerController::IncomingRoleSwitchRequest( |
| model::packets::LinkLayerPacketView incoming) { |
| auto bd_addr = incoming.GetSourceAddress(); |
| auto connection_handle = connections_.GetHandleOnlyAddress(bd_addr); |
| auto switch_req = model::packets::RoleSwitchRequestView::Create(incoming); |
| ASSERT(switch_req.IsValid()); |
| |
| if (connection_handle == kReservedHandle) { |
| INFO(id_, "ignoring Switch Request received on unknown connection"); |
| return; |
| } |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| |
| if (!connection.IsRoleSwitchEnabled()) { |
| INFO(id_, "role switch disabled by local link policy settings"); |
| SendLinkLayerPacket(model::packets::RoleSwitchResponseBuilder::Create( |
| GetAddress(), bd_addr, |
| static_cast<uint8_t>(ErrorCode::ROLE_CHANGE_NOT_ALLOWED))); |
| } else { |
| INFO(id_, "role switch request accepted by local device"); |
| SendLinkLayerPacket(model::packets::RoleSwitchResponseBuilder::Create( |
| GetAddress(), bd_addr, static_cast<uint8_t>(ErrorCode::SUCCESS))); |
| |
| bluetooth::hci::Role new_role = |
| connection.GetRole() == bluetooth::hci::Role::CENTRAL |
| ? bluetooth::hci::Role::PERIPHERAL |
| : bluetooth::hci::Role::CENTRAL; |
| |
| connection.SetRole(new_role); |
| |
| if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| ScheduleTask(kNoDelayMs, [this, bd_addr, new_role]() { |
| send_event_(bluetooth::hci::RoleChangeBuilder::Create( |
| ErrorCode::SUCCESS, bd_addr, new_role)); |
| }); |
| } |
| } |
| } |
| |
| void LinkLayerController::IncomingRoleSwitchResponse( |
| model::packets::LinkLayerPacketView incoming) { |
| auto bd_addr = incoming.GetSourceAddress(); |
| auto connection_handle = connections_.GetHandleOnlyAddress(bd_addr); |
| auto switch_rsp = model::packets::RoleSwitchResponseView::Create(incoming); |
| ASSERT(switch_rsp.IsValid()); |
| |
| if (connection_handle == kReservedHandle) { |
| INFO(id_, "ignoring Switch Response received on unknown connection"); |
| return; |
| } |
| |
| AclConnection& connection = connections_.GetAclConnection(connection_handle); |
| ErrorCode status = ErrorCode(switch_rsp.GetStatus()); |
| bluetooth::hci::Role new_role = |
| status != ErrorCode::SUCCESS ? connection.GetRole() |
| : connection.GetRole() == bluetooth::hci::Role::CENTRAL |
| ? bluetooth::hci::Role::PERIPHERAL |
| : bluetooth::hci::Role::CENTRAL; |
| |
| connection.SetRole(new_role); |
| |
| if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { |
| ScheduleTask(kNoDelayMs, [this, status, bd_addr, new_role]() { |
| send_event_( |
| bluetooth::hci::RoleChangeBuilder::Create(status, bd_addr, new_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::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)) { |
| INFO(id_, "Unknown handle 0x{: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)) { |
| INFO(id_, "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)) { |
| INFO(id_, "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_ = 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); |
| |
| page_ = {}; |
| |
| if (inquiry_timer_task_id_ != kInvalidTaskId) { |
| CancelScheduledTask(inquiry_timer_task_id_); |
| inquiry_timer_task_id_ = kInvalidTaskId; |
| } |
| |
| lm_.reset(link_manager_create(controller_ops_)); |
| ll_.reset(link_layer_create(controller_ops_)); |
| } |
| |
| /// Drive the logic for the Page controller substate. |
| void LinkLayerController::Paging() { |
| auto now = std::chrono::steady_clock::now(); |
| |
| if (page_.has_value() && now >= page_->page_timeout) { |
| INFO("page timeout triggered for connection with {}", |
| page_->bd_addr.ToString()); |
| |
| send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( |
| ErrorCode::PAGE_TIMEOUT, 0, page_->bd_addr, |
| bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); |
| |
| page_ = {}; |
| return; |
| } |
| |
| // Send a Page packet to the peer when a paging interval has passed. |
| // Paging is suppressed while a pending connection with the same peer is |
| // being established (i.e. two hosts initiated a connection simultaneously). |
| if (page_.has_value() && now >= page_->next_page_event && |
| !connections_.HasPendingConnection(page_->bd_addr)) { |
| SendLinkLayerPacket(model::packets::PageBuilder::Create( |
| GetAddress(), page_->bd_addr, class_of_device_, |
| page_->allow_role_switch)); |
| page_->next_page_event = now + kPageInterval; |
| } |
| } |
| |
| 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; |
| } |
| |
| INFO(id_, "Creating SCO connection with {}", bd_addr); |
| |
| // 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; |
| } |
| |
| INFO(id_, "Creating eSCO connection with {}", bd_addr); |
| |
| // 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) { |
| INFO(id_, "Accepting eSCO connection request from {}", bd_addr); |
| |
| if (!connections_.HasPendingScoConnection(bd_addr)) { |
| INFO(id_, "No pending eSCO connection for {}", bd_addr); |
| 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) { |
| INFO(id_, "Rejecting eSCO connection request from {}", bd_addr); |
| |
| 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 sco_bytes = sco_builder->SerializeToBytes(); |
| auto sco_view = bluetooth::hci::ScoView::Create(pdl::packet::slice( |
| std::make_shared<std::vector<uint8_t>>(std::move(sco_bytes)))); |
| ASSERT(sco_view.IsValid()); |
| |
| return SchedulePeriodicTask(0ms, 20ms, [this, address, sco_view]() { |
| INFO(id_, "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 |