diff --git a/model/controller/acl_connection_handler.cc b/model/controller/acl_connection_handler.cc index 65bc303..f472790 100644 --- a/model/controller/acl_connection_handler.cc +++ b/model/controller/acl_connection_handler.cc @@ -92,11 +92,12 @@ uint16_t AclConnectionHandler::CreateLeConnection(AddressWithType addr, AddressWithType resolved_peer, AddressWithType own_addr, bluetooth::hci::Role role, - LeAclConnectionParameters connection_parameters) { + LeAclConnectionParameters connection_parameters, + LeAclSubrateParameters subrate_parameters) { uint16_t handle = GetUnusedHandle(le_acl_connections_, ConnectionHandle::kLeAclRangeStart, ConnectionHandle::kLeAclRangeEnd, last_le_acl_handle_); le_acl_connections_.emplace(handle, LeAclConnection{handle, addr, own_addr, resolved_peer, role, - connection_parameters}); + connection_parameters, subrate_parameters}); return handle; } diff --git a/model/controller/acl_connection_handler.h b/model/controller/acl_connection_handler.h index c3bafa0..6501e07 100644 --- a/model/controller/acl_connection_handler.h +++ b/model/controller/acl_connection_handler.h @@ -63,7 +63,8 @@ class AclConnectionHandler { uint16_t CreateLeConnection(bluetooth::hci::AddressWithType addr, bluetooth::hci::AddressWithType resolved_addr, bluetooth::hci::AddressWithType own_addr, bluetooth::hci::Role role, - LeAclConnectionParameters connection_parameters); + LeAclConnectionParameters connection_parameters, + LeAclSubrateParameters subrate_parameters); bool Disconnect(uint16_t handle, std::function stopStream); diff --git a/model/controller/bredr_controller.cc b/model/controller/bredr_controller.cc index 6397e1a..6151d90 100644 --- a/model/controller/bredr_controller.cc +++ b/model/controller/bredr_controller.cc @@ -57,6 +57,7 @@ namespace rootcanal { constexpr milliseconds kNoDelayMs(0); constexpr milliseconds kPageInterval(1000); +constexpr milliseconds kInquiryInterval(2000); const Address& BrEdrController::GetAddress() const { return address_; } @@ -74,1351 +75,2046 @@ bool BrEdrController::IsEventUnmasked(EventCode event) const { } // ============================================================================= -// BR/EDR Commands +// Link Control commands (Vol 4, Part E § 7.1) // ============================================================================= -// HCI Read Rssi command (Vol 4, Part E § 7.5.4). -ErrorCode BrEdrController::ReadRssi(uint16_t connection_handle, int8_t* rssi) { - if (connections_.HasAclHandle(connection_handle)) { - *rssi = connections_.GetAclConnection(connection_handle).GetRssi(); - return ErrorCode::SUCCESS; +// HCI Inquiry (Vol 4, Part E § 7.1.1). +ErrorCode BrEdrController::Inquiry(uint8_t lap, uint8_t inquiry_length, uint8_t num_responses) { + if (num_responses > 0xff || inquiry_length < 0x1 || inquiry_length > 0x30) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - // Not documented: If the connection handle is not found, the Controller - // shall return the error code Unknown Connection Identifier (0x02). - INFO(id_, "unknown connection identifier"); - return ErrorCode::UNKNOWN_CONNECTION; + if (inquiry_.has_value()) { + INFO(id_, "Inquiry command is already pending"); + return ErrorCode::COMMAND_DISALLOWED; + } + + auto now = std::chrono::steady_clock::now(); + inquiry_ = InquiryState{ + .lap = lap, + .num_responses = num_responses, + .next_inquiry_event = now + kInquiryInterval, + .inquiry_timeout = now + std::chrono::milliseconds(inquiry_length * 1280), + }; + + return ErrorCode::SUCCESS; } -void BrEdrController::SetSecureSimplePairingSupport(bool enable) { - uint64_t bit = 0x1; - secure_simple_pairing_host_support_ = enable; - if (enable) { - host_supported_features_ |= bit; - } else { - host_supported_features_ &= ~bit; +// HCI Inquiry Cancel (Vol 4, Part E § 7.1.2). +ErrorCode BrEdrController::InquiryCancel() { + if (!inquiry_.has_value()) { + INFO(id_, "Inquiry command is not pending"); + return ErrorCode::COMMAND_DISALLOWED; } + + inquiry_ = {}; + return ErrorCode::SUCCESS; } -void BrEdrController::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; +// HCI Create Connection (Vol 4, Part E § 7.1.5). +ErrorCode BrEdrController::CreateConnection(Address bd_addr, uint16_t /* packet_type */, + uint8_t /* page_scan_repetition_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; } -} -void BrEdrController::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; + // Reject the command if a connection already exists + // for the selected peer address. + if (connections_.GetAclConnectionHandle(bd_addr).has_value()) { + INFO(id_, "Connection with {} already exists", bd_addr); + return ErrorCode::CONNECTION_ALREADY_EXISTS; } -} -void BrEdrController::SetLocalName(std::array const& local_name) { - std::copy(local_name.begin(), local_name.end(), local_name_.begin()); -} + // Reject the command if a pending connection already exists + // for the selected peer address. + if (page_scan_.has_value() && page_scan_->bd_addr == bd_addr) { + INFO(id_, "Connection with {} is already being established", bd_addr); + return ErrorCode::CONNECTION_ALREADY_EXISTS; + } -void BrEdrController::SetLocalName(std::vector 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()); -} + auto now = std::chrono::steady_clock::now(); + page_ = PageState{ + .bd_addr = bd_addr, + .allow_role_switch = allow_role_switch, + .next_page_event = now + kPageInterval, + .page_timeout = now + slots(page_timeout_), + }; -void BrEdrController::SetExtendedInquiryResponse( - std::array const& extended_inquiry_response) { - extended_inquiry_response_ = extended_inquiry_response; + return ErrorCode::SUCCESS; } -void BrEdrController::SetExtendedInquiryResponse( - std::vector 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()); -} +// HCI Disconnect (Vol 4, Part E § 7.1.6). +// \p host_reason is taken from the Disconnect command, and sent over +// to the remote as disconnect error. \p controller_reason is the code +// used in the DisconnectionComplete event. +ErrorCode BrEdrController::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); -BrEdrController::BrEdrController(const Address& address, const ControllerProperties& properties, - uint32_t id) - : id_(id), address_(address), properties_(properties), lm_(nullptr, link_manager_destroy) { - controller_ops_ = { - .user_pointer = this, - .get_handle = - [](void* user, const uint8_t (*address)[6]) { - auto controller = static_cast(user); + SendLinkLayerPacket(model::packets::ScoDisconnectBuilder::Create( + GetAddress(), remote, static_cast(host_reason))); - // Returns the connection handle but only for established - // BR-EDR connections. - return controller->connections_.GetAclConnectionHandle(Address(*address)) - .value_or(-1); - }, + connections_.Disconnect(handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); + SendDisconnectionCompleteEvent(handle, controller_reason); + return ErrorCode::SUCCESS; + } - .get_address = - [](void* user, uint16_t handle, uint8_t (*result)[6]) { - auto controller = static_cast(user); - Address address = {}; + if (connections_.HasAclHandle(handle)) { + auto connection = connections_.GetAclConnection(handle); + auto address = connection.address; + INFO(id_, "Disconnecting ACL connection with {}", connection.address); - if (controller->connections_.HasAclHandle(handle)) { - address = controller->connections_.GetAclConnection(handle).address; - } else if (controller->connections_.HasLeAclHandle(handle)) { - address = controller->connections_.GetLeAclConnection(handle) - .address.GetAddress(); - } + auto sco_handle = connections_.GetScoConnectionHandle(connection.address); + if (sco_handle.has_value()) { + SendLinkLayerPacket(model::packets::ScoDisconnectBuilder::Create( + connection.own_address, connection.address, static_cast(host_reason))); - std::copy(address.data(), address.data() + 6, - reinterpret_cast(result)); - }, + connections_.Disconnect(*sco_handle, + [this](TaskId task_id) { CancelScheduledTask(task_id); }); + SendDisconnectionCompleteEvent(*sco_handle, controller_reason); + } - .get_extended_features = - [](void* user, uint8_t features_page) { - auto controller = static_cast(user); - return controller->GetLmpFeatures(features_page); - }, + SendLinkLayerPacket(model::packets::DisconnectBuilder::Create( + connection.own_address, connection.address, static_cast(host_reason))); - .send_hci_event = - [](void* user, const uint8_t* data, uintptr_t len) { - auto controller = static_cast(user); + connections_.Disconnect(handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); + SendDisconnectionCompleteEvent(handle, controller_reason); - auto event_code = static_cast(data[0]); - controller->send_event_(bluetooth::hci::EventBuilder::Create( - event_code, std::vector(data + 2, data + len))); - }, + ASSERT(link_manager_remove_link(lm_.get(), reinterpret_cast(address.data()))); + return ErrorCode::SUCCESS; + } - .send_lmp_packet = - [](void* user, const uint8_t (*to)[6], const uint8_t* data, uintptr_t len) { - auto controller = static_cast(user); + return ErrorCode::UNKNOWN_CONNECTION; +} - Address source = controller->GetAddress(); - Address dest(*to); +// HCI Create Connection Cancel (Vol 4, Part E § 7.1.7). +ErrorCode BrEdrController::CreateConnectionCancel(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; + } - controller->SendLinkLayerPacket(model::packets::LmpBuilder::Create( - source, dest, std::vector(data, data + len))); - }}; + // 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)); + }); + } - lm_.reset(link_manager_create(controller_ops_)); + page_ = {}; + return ErrorCode::SUCCESS; } -BrEdrController::~BrEdrController() {} +// HCI Accept Connection Request (Vol 4, Part E § 7.1.8). +ErrorCode BrEdrController::AcceptConnectionRequest(Address bd_addr, bool try_role_switch) { + if (page_scan_.has_value() && page_scan_->bd_addr == 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); + }); -void BrEdrController::SendLinkLayerPacket( - std::unique_ptr packet, int8_t tx_power) { - std::shared_ptr shared_packet = std::move(packet); - ScheduleTask(kNoDelayMs, [this, shared_packet, tx_power]() { - send_to_remote_(shared_packet, Phy::Type::BR_EDR, tx_power); - }); -} + return ErrorCode::SUCCESS; + } -ErrorCode BrEdrController::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(); - SendLinkLayerPacket(model::packets::ReadRemoteExtendedFeaturesBuilder::Create( - own_address, peer_address, page_number)); - } 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(opcode)); - return ErrorCode::UNKNOWN_HCI_COMMAND; + // 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 = *connections_.GetScoConnectionHandle(bd_addr); + ScoLinkParameters link_parameters = {}; + ScoConnectionParameters connection_parameters = + connections_.GetScoConnectionParameters(bd_addr); + + if (!connections_.AcceptPendingScoConnection(bd_addr, connection_parameters, [this, bd_addr] { + return BrEdrController::StartScoStream(bd_addr); + })) { + connections_.CancelPendingScoConnection(bd_addr); + status = ErrorCode::SCO_INTERVAL_REJECTED; // TODO: proper status code + sco_handle = 0; + } else { + 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; } - return ErrorCode::SUCCESS; + INFO(id_, "No pending connection for {}", bd_addr); + return ErrorCode::UNKNOWN_CONNECTION; } -ErrorCode BrEdrController::SendCommandToRemoteByHandle(OpCode opcode, pdl::packet::slice args, - uint16_t handle) { - if (!connections_.HasAclHandle(handle)) { +// HCI Accept Connection Request (Vol 4, Part E § 7.1.9). +ErrorCode BrEdrController::RejectConnectionRequest(Address bd_addr, uint8_t reason) { + if (!page_scan_.has_value() || page_scan_->bd_addr != bd_addr) { + INFO(id_, "No pending connection for {}", bd_addr); return ErrorCode::UNKNOWN_CONNECTION; } - auto const& connection = connections_.GetAclConnection(handle); - return SendCommandToRemoteByAddress(opcode, args, connection.own_address, connection.address); + ScheduleTask(kNoDelayMs, + [this, bd_addr, reason]() { RejectPeripheralConnection(bd_addr, reason); }); + + return ErrorCode::SUCCESS; } -ErrorCode BrEdrController::SendScoToRemote(bluetooth::hci::ScoView sco_packet) { - uint16_t handle = sco_packet.GetHandle(); - if (!connections_.HasScoHandle(handle)) { +// HCI Change Connection Packet Type (Vol 4, Part E § 7.1.14). +ErrorCode BrEdrController::ChangeConnectionPacketType(uint16_t connection_handle, + uint16_t packet_type) { + if (!connections_.HasAclHandle(connection_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 sco_data_bytes(sco_data.begin(), sco_data.end()); + ScheduleTask(kNoDelayMs, [this, connection_handle, packet_type]() { + if (IsEventUnmasked(EventCode::CONNECTION_PACKET_TYPE_CHANGED)) { + send_event_(bluetooth::hci::ConnectionPacketTypeChangedBuilder::Create( + ErrorCode::SUCCESS, connection_handle, packet_type)); + } + }); - SendLinkLayerPacket( - model::packets::ScoBuilder::Create(source, destination, std::move(sco_data_bytes))); return ErrorCode::SUCCESS; } -void BrEdrController::IncomingPacket(model::packets::LinkLayerPacketView incoming, int8_t rssi) { - ASSERT(incoming.IsValid()); - auto destination_address = incoming.GetDestinationAddress(); - auto source_address = incoming.GetSourceAddress(); - - // Accept broadcasts to address 00:00:00:00:00:00 but otherwise ignore the incoming - // packet if the destination address is not the local public address. - if (destination_address != Address::kEmpty && destination_address != address_) { - DEBUG(id_, "[LM] {} | Dropping {} packet not addressed to me {}->{}", address_, - PacketTypeText(incoming.GetType()), source_address, destination_address); - return; - } - - // Update link timeout for established ACL connections. - auto connection_handle = connections_.GetAclConnectionHandle(source_address); - if (connection_handle.has_value()) { - connections_.GetAclConnection(*connection_handle).ResetLinkTimer(); +// HCI Change Connection Link Key (Vol 4, Part E § 7.1.17). +ErrorCode BrEdrController::ChangeConnectionLinkKey(uint16_t connection_handle) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } - switch (incoming.GetType()) { - case model::packets::PacketType::ACL: - IncomingAclPacket(incoming, rssi); - break; - case model::packets::PacketType::SCO: - IncomingScoPacket(incoming); - break; - case model::packets::PacketType::DISCONNECT: - IncomingDisconnectPacket(incoming); - break; - case model::packets::PacketType::LMP: - IncomingLmpPacket(incoming); - break; - case model::packets::PacketType::INQUIRY: - IncomingInquiryPacket(incoming, rssi); - break; - case model::packets::PacketType::INQUIRY_RESPONSE: - IncomingInquiryResponsePacket(incoming); - break; - case model::packets::PacketType::PAGE: - 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; - default: - WARNING(id_, "Dropping unhandled packet of type {}", - model::packets::PacketTypeText(incoming.GetType())); - } + // TODO: implement real logic + return ErrorCode::COMMAND_DISALLOWED; } -void BrEdrController::IncomingAclPacket(model::packets::LinkLayerPacketView incoming, int8_t rssi) { - auto acl = model::packets::AclView::Create(incoming); - ASSERT(acl.IsValid()); +// HCI Remote Name Request (Vol 4, Part E § 7.1.19). +ErrorCode BrEdrController::RemoteNameRequest(Address bd_addr, uint8_t /*page_scan_repetition_mode*/, + uint16_t /*clock_offset*/) { + // LMP features get requested with remote name requests. + SendLinkLayerPacket(model::packets::ReadRemoteLmpFeaturesBuilder::Create(GetAddress(), bd_addr)); + SendLinkLayerPacket(model::packets::RemoteNameRequestBuilder::Create(GetAddress(), bd_addr)); - auto acl_data = acl.GetData(); - auto packet_boundary_flag = bluetooth::hci::PacketBoundaryFlag(acl.GetPacketBoundaryFlag()); - auto broadcast_flag = bluetooth::hci::BroadcastFlag(acl.GetBroadcastFlag()); + return ErrorCode::SUCCESS; +} - if (packet_boundary_flag == - bluetooth::hci::PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) { - packet_boundary_flag = bluetooth::hci::PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE; +// HCI Read Remote Supported Features (Vol 4, Part E § 7.1.21). +ErrorCode BrEdrController::ReadRemoteSupportedFeatures(uint16_t connection_handle) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } - INFO(id_, "ACL Packet [{}] {} -> {}", acl_data.size(), incoming.GetSourceAddress(), - incoming.GetDestinationAddress()); + auto& connection = connections_.GetAclConnection(connection_handle); + SendLinkLayerPacket(model::packets::ReadRemoteSupportedFeaturesBuilder::Create( + connection.own_address, connection.address)); + return ErrorCode::SUCCESS; +} - auto connection_handle = connections_.GetAclConnectionHandle(incoming.GetSourceAddress()); - if (!connection_handle.has_value()) { - INFO(id_, "Dropping packet since connection does not exist"); - return; +// HCI Read Remote Extended Features (Vol 4, Part E § 7.1.22). +ErrorCode BrEdrController::ReadRemoteExtendedFeatures(uint16_t connection_handle, + uint8_t page_number) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } - // Update the RSSI for the local ACL connection. - auto& connection = connections_.GetAclConnection(*connection_handle); - connection.SetRssi(rssi); - - send_acl_(bluetooth::hci::AclBuilder::Create( - *connection_handle, packet_boundary_flag, broadcast_flag, - std::vector(acl_data.begin(), acl_data.end()))); + auto& connection = connections_.GetAclConnection(connection_handle); + SendLinkLayerPacket(model::packets::ReadRemoteExtendedFeaturesBuilder::Create( + connection.own_address, connection.address, page_number)); + return ErrorCode::SUCCESS; } -void BrEdrController::IncomingScoPacket(model::packets::LinkLayerPacketView incoming) { - Address source = incoming.GetSourceAddress(); - auto sco_handle = connections_.GetScoConnectionHandle(source); - if (!sco_handle.has_value()) { - INFO(id_, "Spurious SCO packet from {}", source); - return; +// HCI Read Remote Version Information (Vol 4, Part E § 7.1.23). +ErrorCode BrEdrController::ReadRemoteVersionInformation(uint16_t connection_handle) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } - auto sco = model::packets::ScoView::Create(incoming); - ASSERT(sco.IsValid()); - auto sco_data = sco.GetPayload(); - std::vector sco_data_bytes(sco_data.begin(), sco_data.end()); - - INFO(id_, "Sco Packet [{}] {} -> {}", static_cast(sco_data_bytes.size()), - incoming.GetSourceAddress(), incoming.GetDestinationAddress()); - - send_sco_(bluetooth::hci::ScoBuilder::Create( - *sco_handle, bluetooth::hci::PacketStatusFlag::CORRECTLY_RECEIVED, sco_data_bytes)); + auto& connection = connections_.GetAclConnection(connection_handle); + SendLinkLayerPacket(model::packets::ReadRemoteVersionInformationBuilder::Create( + connection.own_address, connection.address)); + return ErrorCode::SUCCESS; } -void BrEdrController::IncomingRemoteNameRequest(model::packets::LinkLayerPacketView incoming) { - auto view = model::packets::RemoteNameRequestView::Create(incoming); - ASSERT(view.IsValid()); +// HCI Read Clock Offset (Vol 4, Part E § 7.1.24). +ErrorCode BrEdrController::ReadClockOffset(uint16_t connection_handle) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; + } - SendLinkLayerPacket(model::packets::RemoteNameRequestResponseBuilder::Create( - incoming.GetDestinationAddress(), incoming.GetSourceAddress(), local_name_)); + auto& connection = connections_.GetAclConnection(connection_handle); + SendLinkLayerPacket(model::packets::ReadClockOffsetBuilder::Create(connection.own_address, + connection.address)); + return ErrorCode::SUCCESS; } -void BrEdrController::IncomingRemoteNameRequestResponse( - model::packets::LinkLayerPacketView incoming) { - auto view = model::packets::RemoteNameRequestResponseView::Create(incoming); - ASSERT(view.IsValid()); +// HCI Add SCO Connection. +// Deprecated in the Core specification v1.2, removed in v4.2. +// Support is provided to satisfy PTS tester requirements. +ErrorCode BrEdrController::AddScoConnection(uint16_t connection_handle, uint16_t packet_type) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; + } - if (IsEventUnmasked(EventCode::REMOTE_NAME_REQUEST_COMPLETE)) { - send_event_(bluetooth::hci::RemoteNameRequestCompleteBuilder::Create( - ErrorCode::SUCCESS, incoming.GetSourceAddress(), view.GetName())); + auto const& connection = connections_.GetAclConnection(connection_handle); + if (connections_.HasPendingScoConnection(connection.address)) { + return ErrorCode::COMMAND_DISALLOWED; } -} -void BrEdrController::IncomingReadRemoteLmpFeatures(model::packets::LinkLayerPacketView incoming) { - SendLinkLayerPacket(model::packets::ReadRemoteLmpFeaturesResponseBuilder::Create( - incoming.GetDestinationAddress(), incoming.GetSourceAddress(), host_supported_features_)); -} + INFO(id_, "Creating SCO connection with {}", connection.address); -void BrEdrController::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())); - } -} + // 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(connection.address, connection_parameters, SCO_STATE_PENDING, + ScoDatapath::NORMAL, true); -void BrEdrController::IncomingReadRemoteSupportedFeatures( - model::packets::LinkLayerPacketView incoming) { - SendLinkLayerPacket(model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create( - incoming.GetDestinationAddress(), incoming.GetSourceAddress(), - properties_.lmp_features[0])); + // Send SCO connection request to peer. + SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create( + GetAddress(), connection.address, 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; } -void BrEdrController::IncomingReadRemoteSupportedFeaturesResponse( - model::packets::LinkLayerPacketView incoming) { - auto view = model::packets::ReadRemoteSupportedFeaturesResponseView::Create(incoming); - ASSERT(view.IsValid()); - Address source = incoming.GetSourceAddress(); - auto handle = connections_.GetAclConnectionHandle(source); - if (!handle.has_value()) { - 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())); +// HCI Setup Synchronous Connection (Vol 4, Part E § 7.1.26). +ErrorCode BrEdrController::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_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } -} -void BrEdrController::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(ErrorCode::SUCCESS); - if (page_number >= properties_.lmp_features.size()) { - error_code = static_cast(ErrorCode::INVALID_LMP_OR_LL_PARAMETERS); + auto const& connection = connections_.GetAclConnection(connection_handle); + if (connections_.HasPendingScoConnection(connection.address)) { + // 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; } - SendLinkLayerPacket(model::packets::ReadRemoteExtendedFeaturesResponseBuilder::Create( - incoming.GetDestinationAddress(), incoming.GetSourceAddress(), error_code, page_number, - GetMaxLmpFeaturesPageNumber(), GetLmpFeatures(page_number))); -} -void BrEdrController::IncomingReadRemoteExtendedFeaturesResponse( - model::packets::LinkLayerPacketView incoming) { - auto view = model::packets::ReadRemoteExtendedFeaturesResponseView::Create(incoming); - ASSERT(view.IsValid()); - Address source = incoming.GetSourceAddress(); - auto handle = connections_.GetAclConnectionHandle(source); - if (!handle.has_value()) { - 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(view.GetStatus()), *handle, view.GetPageNumber(), - view.GetMaxPageNumber(), view.GetFeatures())); - } -} + INFO(id_, "Creating eSCO connection with {}", connection.address); -void BrEdrController::IncomingReadRemoteVersion(model::packets::LinkLayerPacketView incoming) { - SendLinkLayerPacket(model::packets::ReadRemoteVersionInformationResponseBuilder::Create( - incoming.GetDestinationAddress(), incoming.GetSourceAddress(), - static_cast(properties_.lmp_version), - static_cast(properties_.lmp_subversion), properties_.company_identifier)); -} + // Save connection parameters. + ScoConnectionParameters connection_parameters = {transmit_bandwidth, receive_bandwidth, + max_latency, voice_setting, + retransmission_effort, packet_types}; + connections_.CreateScoConnection(connection.address, connection_parameters, SCO_STATE_PENDING, + datapath); -void BrEdrController::IncomingReadRemoteVersionResponse( - model::packets::LinkLayerPacketView incoming) { - auto view = model::packets::ReadRemoteVersionInformationResponseView::Create(incoming); - ASSERT(view.IsValid()); - Address source = incoming.GetSourceAddress(); + // Send eSCO connection request to peer. + SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create( + GetAddress(), connection.address, transmit_bandwidth, receive_bandwidth, max_latency, + voice_setting, retransmission_effort, packet_types, class_of_device_)); + return ErrorCode::SUCCESS; +} - auto handle = connections_.GetAclConnectionHandle(source); +// HCI Accept Synchronous Connection (Vol 4, Part E § 7.1.26). +ErrorCode BrEdrController::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 (!handle.has_value()) { - INFO(id_, "Discarding response from a disconnected device {}", source); - return; + if (!connections_.HasPendingScoConnection(bd_addr)) { + INFO(id_, "No pending eSCO connection for {}", bd_addr); + return ErrorCode::COMMAND_DISALLOWED; } - if (IsEventUnmasked(EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE)) { - send_event_(bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create( - ErrorCode::SUCCESS, *handle, view.GetLmpVersion(), view.GetManufacturerName(), - view.GetLmpSubversion())); + ErrorCode status = ErrorCode::SUCCESS; + uint16_t sco_handle = *connections_.GetScoConnectionHandle(bd_addr); + 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 BrEdrController::StartScoStream(bd_addr); + })) { + connections_.CancelPendingScoConnection(bd_addr); + status = ErrorCode::STATUS_UNKNOWN; // TODO: proper status code + sco_handle = 0; + } else { + link_parameters = connections_.GetScoLinkParameters(bd_addr); } -} -void BrEdrController::IncomingReadClockOffset(model::packets::LinkLayerPacketView incoming) { - SendLinkLayerPacket(model::packets::ReadClockOffsetResponseBuilder::Create( - incoming.GetDestinationAddress(), incoming.GetSourceAddress(), GetClockOffset())); + // 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; } -void BrEdrController::IncomingReadClockOffsetResponse( - model::packets::LinkLayerPacketView incoming) { - auto view = model::packets::ReadClockOffsetResponseView::Create(incoming); - ASSERT(view.IsValid()); - Address source = incoming.GetSourceAddress(); - auto handle = connections_.GetAclConnectionHandle(source); - if (!handle.has_value()) { - INFO(id_, "Discarding response from a disconnected device {}", source); - return; +// HCI Reject Synchronous Connection (Vol 4, Part E § 7.1.27). +ErrorCode BrEdrController::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 (IsEventUnmasked(EventCode::READ_CLOCK_OFFSET_COMPLETE)) { - send_event_(bluetooth::hci::ReadClockOffsetCompleteBuilder::Create(ErrorCode::SUCCESS, *handle, - view.GetOffset())); + 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 BrEdrController::IncomingDisconnectPacket(model::packets::LinkLayerPacketView incoming) { - INFO(id_, "Disconnect Packet"); +// HCI Enhanced Setup Synchronous Connection (Vol 4, Part E § 7.1.45). +ErrorCode BrEdrController::EnhancedSetupSynchronousConnection( + uint16_t connection_handle, uint32_t transmit_bandwidth, uint32_t receive_bandwidth, + bluetooth::hci::ScoCodingFormat transmit_coding_format, + bluetooth::hci::ScoCodingFormat receive_coding_format, + uint16_t /*transmit_codec_frame_size*/, uint16_t /*receive_codec_frame_size*/, + uint32_t input_bandwidth, uint32_t output_bandwidth, + bluetooth::hci::ScoCodingFormat input_coding_format, + bluetooth::hci::ScoCodingFormat output_coding_format, uint16_t /*input_coded_data_size*/, + uint16_t /*output_coded_data_size*/, + bluetooth::hci::ScoPcmDataFormat /*input_pcm_data_format*/, + bluetooth::hci::ScoPcmDataFormat /*output_pcm_data_format*/, + uint8_t /*input_pcm_sample_payload_msb_position*/, + uint8_t /*output_pcm_sample_payload_msb_position*/, + bluetooth::hci::ScoDataPath input_data_path, bluetooth::hci::ScoDataPath output_data_path, + uint8_t /*input_transport_unit_size*/, uint8_t /*output_transport_unit_size*/, + uint16_t max_latency, uint16_t packet_type, + bluetooth::hci::RetransmissionEffort retransmission_effort) { + // The Host shall set the Transmit_Coding_Format and Receive_Coding_Formats + // to be equal. + if (transmit_coding_format.coding_format_ != receive_coding_format.coding_format_ || + transmit_coding_format.company_id_ != receive_coding_format.company_id_ || + transmit_coding_format.vendor_specific_codec_id_ != + receive_coding_format.vendor_specific_codec_id_) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Transmit_Coding_Format " + "({}) and Receive_Coding_Format ({}) as they are not equal", + transmit_coding_format.ToString(), receive_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - auto disconnect = model::packets::DisconnectView::Create(incoming); - ASSERT(disconnect.IsValid()); + // The Host shall either set the Input_Bandwidth and Output_Bandwidth + // to be equal, or shall set one of them to be zero and the other non-zero. + if (input_bandwidth != output_bandwidth && input_bandwidth != 0 && output_bandwidth != 0) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Input_Bandwidth ({})" + " and Output_Bandwidth ({}) as they are not equal and different from 0", + input_bandwidth, output_bandwidth); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - Address peer = incoming.GetSourceAddress(); - auto handle = connections_.GetAclConnectionHandle(peer); - if (!handle.has_value()) { - INFO(id_, "Discarding disconnect from a disconnected device {}", peer); - return; + // The Host shall set the Input_Coding_Format and Output_Coding_Format + // to be equal. + if (input_coding_format.coding_format_ != output_coding_format.coding_format_ || + input_coding_format.company_id_ != output_coding_format.company_id_ || + input_coding_format.vendor_specific_codec_id_ != + output_coding_format.vendor_specific_codec_id_) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Input_Coding_Format ({})" + " and Output_Coding_Format ({}) as they are not equal", + input_coding_format.ToString(), output_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - ASSERT_LOG(connections_.Disconnect(*handle, - [this](TaskId task_id) { CancelScheduledTask(task_id); }), - "GetHandle() returned invalid handle 0x{:x}", *handle); + // Root-Canal does not implement audio data transport paths other than the + // default HCI transport - other transports will receive spoofed data + ScoDatapath datapath = ScoDatapath::NORMAL; + if (input_data_path != bluetooth::hci::ScoDataPath::HCI || + output_data_path != bluetooth::hci::ScoDataPath::HCI) { + WARNING(id_, + "EnhancedSetupSynchronousConnection: Input_Data_Path ({})" + " and/or Output_Data_Path ({}) are not over HCI, so data will be " + "spoofed", + static_cast(input_data_path), static_cast(output_data_path)); + datapath = ScoDatapath::SPOOFED; + } + + // Either both the Transmit_Coding_Format and Input_Coding_Format shall be + // “transparent” or neither shall be. If both are “transparent”, the + // Transmit_Bandwidth and the Input_Bandwidth shall be the same and the + // Controller shall not modify the data sent to the remote device. + if (transmit_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + transmit_bandwidth != input_bandwidth) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Transmit_Bandwidth ({})" + " and Input_Bandwidth ({}) as they are not equal", + transmit_bandwidth, input_bandwidth); + INFO(id_, + "EnhancedSetupSynchronousConnection: the Transmit_Bandwidth and " + "Input_Bandwidth shall be equal when both Transmit_Coding_Format " + "and Input_Coding_Format are 'transparent'"); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + if ((transmit_coding_format.coding_format_ == + bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != + (input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Transmit_Coding_Format " + "({}) and Input_Coding_Format ({}) as they are incompatible", + transmit_coding_format.ToString(), input_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - uint8_t reason = disconnect.GetReason(); - ASSERT(link_manager_remove_link(lm_.get(), reinterpret_cast(peer.data()))); - SendDisconnectionCompleteEvent(*handle, ErrorCode(reason)); -} + // Either both the Receive_Coding_Format and Output_Coding_Format shall + // be “transparent” or neither shall be. If both are “transparent”, the + // Receive_Bandwidth and the Output_Bandwidth shall be the same and the + // Controller shall not modify the data sent to the Host. + if (receive_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + receive_bandwidth != output_bandwidth) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Receive_Bandwidth ({})" + " and Output_Bandwidth ({}) as they are not equal", + receive_bandwidth, output_bandwidth); + INFO(id_, + "EnhancedSetupSynchronousConnection: the Receive_Bandwidth and " + "Output_Bandwidth shall be equal when both Receive_Coding_Format " + "and Output_Coding_Format are 'transparent'"); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + if ((receive_coding_format.coding_format_ == + bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != + (output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Receive_Coding_Format " + "({}) and Output_Coding_Format ({}) as they are incompatible", + receive_coding_format.ToString(), output_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } -void BrEdrController::IncomingInquiryPacket(model::packets::LinkLayerPacketView incoming, - uint8_t rssi) { - if (!inquiry_scan_enable_) { - return; + return SetupSynchronousConnection( + connection_handle, transmit_bandwidth, receive_bandwidth, max_latency, GetVoiceSetting(), + static_cast(retransmission_effort), packet_type, datapath); +} + +// HCI Enhanced Accept Synchronous Connection (Vol 4, Part E § 7.1.46). +ErrorCode BrEdrController::EnhancedAcceptSynchronousConnection( + Address bd_addr, uint32_t transmit_bandwidth, uint32_t receive_bandwidth, + bluetooth::hci::ScoCodingFormat transmit_coding_format, + bluetooth::hci::ScoCodingFormat receive_coding_format, + uint16_t /*transmit_codec_frame_size*/, uint16_t /*receive_codec_frame_size*/, + uint32_t input_bandwidth, uint32_t output_bandwidth, + bluetooth::hci::ScoCodingFormat input_coding_format, + bluetooth::hci::ScoCodingFormat output_coding_format, uint16_t /*input_coded_data_size*/, + uint16_t /*output_coded_data_size*/, + bluetooth::hci::ScoPcmDataFormat /*input_pcm_data_format*/, + bluetooth::hci::ScoPcmDataFormat /*output_pcm_data_format*/, + uint8_t /*input_pcm_sample_payload_msb_position*/, + uint8_t /*output_pcm_sample_payload_msb_position*/, + bluetooth::hci::ScoDataPath input_data_path, bluetooth::hci::ScoDataPath output_data_path, + uint8_t /*input_transport_unit_size*/, uint8_t /*output_transport_unit_size*/, + uint16_t max_latency, uint16_t packet_type, + bluetooth::hci::RetransmissionEffort retransmission_effort) { + // The Host shall set the Transmit_Coding_Format and Receive_Coding_Formats + // to be equal. + if (transmit_coding_format.coding_format_ != receive_coding_format.coding_format_ || + transmit_coding_format.company_id_ != receive_coding_format.company_id_ || + transmit_coding_format.vendor_specific_codec_id_ != + receive_coding_format.vendor_specific_codec_id_) { + INFO(id_, + "EnhancedAcceptSynchronousConnection: rejected Transmit_Coding_Format " + "({})" + " and Receive_Coding_Format ({}) as they are not equal", + transmit_coding_format.ToString(), receive_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - auto inquiry = model::packets::InquiryView::Create(incoming); - ASSERT(inquiry.IsValid()); + // The Host shall either set the Input_Bandwidth and Output_Bandwidth + // to be equal, or shall set one of them to be zero and the other non-zero. + if (input_bandwidth != output_bandwidth && input_bandwidth != 0 && output_bandwidth != 0) { + INFO(id_, + "EnhancedAcceptSynchronousConnection: rejected Input_Bandwidth ({})" + " and Output_Bandwidth ({}) as they are not equal and different from 0", + input_bandwidth, output_bandwidth); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - Address peer = incoming.GetSourceAddress(); - uint8_t lap = inquiry.GetLap(); + // The Host shall set the Input_Coding_Format and Output_Coding_Format + // to be equal. + if (input_coding_format.coding_format_ != output_coding_format.coding_format_ || + input_coding_format.company_id_ != output_coding_format.company_id_ || + input_coding_format.vendor_specific_codec_id_ != + output_coding_format.vendor_specific_codec_id_) { + INFO(id_, + "EnhancedAcceptSynchronousConnection: rejected Input_Coding_Format ({})" + " and Output_Coding_Format ({}) as they are not equal", + input_coding_format.ToString(), output_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - // 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; + // Root-Canal does not implement audio data transport paths other than the + // default HCI transport. + if (input_data_path != bluetooth::hci::ScoDataPath::HCI || + output_data_path != bluetooth::hci::ScoDataPath::HCI) { + INFO(id_, + "EnhancedSetupSynchronousConnection: Input_Data_Path ({})" + " and/or Output_Data_Path ({}) are not over HCI, so data will be " + "spoofed", + static_cast(input_data_path), static_cast(output_data_path)); + } + + // Either both the Transmit_Coding_Format and Input_Coding_Format shall be + // “transparent” or neither shall be. If both are “transparent”, the + // Transmit_Bandwidth and the Input_Bandwidth shall be the same and the + // Controller shall not modify the data sent to the remote device. + if (transmit_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + transmit_bandwidth != input_bandwidth) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Transmit_Bandwidth ({})" + " and Input_Bandwidth ({}) as they are not equal", + transmit_bandwidth, input_bandwidth); + INFO(id_, + "EnhancedSetupSynchronousConnection: the Transmit_Bandwidth and " + "Input_Bandwidth shall be equal when both Transmit_Coding_Format " + "and Input_Coding_Format are 'transparent'"); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + if ((transmit_coding_format.coding_format_ == + bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != + (input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Transmit_Coding_Format " + "({}) and Input_Coding_Format ({}) as they are incompatible", + transmit_coding_format.ToString(), input_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - switch (inquiry.GetInquiryType()) { - case (model::packets::InquiryType::STANDARD): { - SendLinkLayerPacket(model::packets::InquiryResponseBuilder::Create( - GetAddress(), peer, static_cast(GetPageScanRepetitionMode()), - class_of_device_, GetClockOffset())); - } break; - case (model::packets::InquiryType::RSSI): { - SendLinkLayerPacket(model::packets::InquiryResponseWithRssiBuilder::Create( - GetAddress(), peer, static_cast(GetPageScanRepetitionMode()), - class_of_device_, GetClockOffset(), rssi)); - } break; - case (model::packets::InquiryType::EXTENDED): { - SendLinkLayerPacket(model::packets::ExtendedInquiryResponseBuilder::Create( - GetAddress(), peer, static_cast(GetPageScanRepetitionMode()), - class_of_device_, GetClockOffset(), rssi, extended_inquiry_response_)); - } break; - default: - WARNING(id_, "Unhandled Incoming Inquiry of type {}", static_cast(inquiry.GetType())); - return; + // Either both the Receive_Coding_Format and Output_Coding_Format shall + // be “transparent” or neither shall be. If both are “transparent”, the + // Receive_Bandwidth and the Output_Bandwidth shall be the same and the + // Controller shall not modify the data sent to the Host. + if (receive_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && + receive_bandwidth != output_bandwidth) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Receive_Bandwidth ({})" + " and Output_Bandwidth ({}) as they are not equal", + receive_bandwidth, output_bandwidth); + INFO(id_, + "EnhancedSetupSynchronousConnection: the Receive_Bandwidth and " + "Output_Bandwidth shall be equal when both Receive_Coding_Format " + "and Output_Coding_Format are 'transparent'"); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + if ((receive_coding_format.coding_format_ == + bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != + (output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { + INFO(id_, + "EnhancedSetupSynchronousConnection: rejected Receive_Coding_Format " + "({}) and Output_Coding_Format ({}) as they are incompatible", + receive_coding_format.ToString(), output_coding_format.ToString()); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - // TODO: Send an Inquiry Response Notification Event 7.7.74 -} -void BrEdrController::IncomingInquiryResponsePacket(model::packets::LinkLayerPacketView incoming) { - auto basic_inquiry_response = model::packets::BasicInquiryResponseView::Create(incoming); - ASSERT(basic_inquiry_response.IsValid()); - std::vector eir; + return AcceptSynchronousConnection(bd_addr, transmit_bandwidth, receive_bandwidth, max_latency, + GetVoiceSetting(), static_cast(retransmission_effort), + packet_type); +} - 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()); +// ============================================================================= +// Link Policy commands (Vol 4, Part E § 7.2) +// ============================================================================= - auto page_scan_repetition_mode = - (bluetooth::hci::PageScanRepetitionMode)inquiry_response.GetPageScanRepetitionMode(); +// HCI Hold Mode command (Vol 4, Part E § 7.2.1). +ErrorCode BrEdrController::HoldMode(uint16_t connection_handle, uint16_t hold_mode_max_interval, + uint16_t hold_mode_min_interval) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; + } - std::vector 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()); + if (hold_mode_max_interval < hold_mode_min_interval) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - auto page_scan_repetition_mode = - (bluetooth::hci::PageScanRepetitionMode)inquiry_response.GetPageScanRepetitionMode(); + // TODO: implement real logic + return ErrorCode::COMMAND_DISALLOWED; +} - std::vector 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; +// HCI Sniff Mode command (Vol 4, Part E § 7.2.2). +ErrorCode BrEdrController::SniffMode(uint16_t connection_handle, uint16_t sniff_max_interval, + uint16_t sniff_min_interval, uint16_t sniff_attempt, + uint16_t sniff_timeout) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; + } - case (model::packets::InquiryType::EXTENDED): { - auto inquiry_response = - model::packets::ExtendedInquiryResponseView::Create(basic_inquiry_response); - ASSERT(inquiry_response.IsValid()); + if (sniff_max_interval < sniff_min_interval || sniff_attempt < 0x0001 || sniff_attempt > 0x7FFF || + sniff_timeout > 0x7FFF) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - send_event_(bluetooth::hci::ExtendedInquiryResultBuilder::Create( - inquiry_response.GetSourceAddress(), - static_cast( - inquiry_response.GetPageScanRepetitionMode()), - inquiry_response.GetClassOfDevice(), inquiry_response.GetClockOffset(), - inquiry_response.GetRssi(), inquiry_response.GetExtendedInquiryResponse())); - } break; + // TODO: implement real logic + return ErrorCode::COMMAND_DISALLOWED; +} - default: - WARNING(id_, "Unhandled Incoming Inquiry Response of type {}", - static_cast(basic_inquiry_response.GetInquiryType())); +// HCI Exit Sniff Mode command (Vol 4, Part E § 7.2.3). +ErrorCode BrEdrController::ExitSniffMode(uint16_t connection_handle) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } -} -void BrEdrController::IncomingScoConnectionRequest(model::packets::LinkLayerPacketView incoming) { - Address address = incoming.GetSourceAddress(); - auto request = model::packets::ScoConnectionRequestView::Create(incoming); - ASSERT(request.IsValid()); + // TODO: implement real logic + return ErrorCode::COMMAND_DISALLOWED; +} - INFO(id_, "Received eSCO connection request from {}", address); +// HCI QoS Setup command (Vol 4, Part E § 7.2.6). +ErrorCode BrEdrController::QosSetup(uint16_t connection_handle, uint8_t service_type, + uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency, + uint32_t delay_variation) { + // The Connection_Handle shall be a Connection_Handle for an ACL connection. + if (!connections_.HasAclHandle(connection_handle)) { + INFO(id_, "unknown connection handle {}", connection_handle); + return ErrorCode::UNKNOWN_CONNECTION; + } - // 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); + // This field indicates the level of service required. The list below defines the different + // services available. The default value is ‘Best effort’. + // - 0x00 No traffic + // - 0x01 Best effort (Default) + // - 0x02 Guaranteed + if (service_type > 0x02) { + INFO(id_, "invalid service_type 0x{:02x}", service_type); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create( - GetAddress(), address, (uint8_t)ErrorCode::SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED, 0, 0, - 0, 0, 0, 0)); - return; + // When the Link Manager has completed the LMP messages to establish the requested QoS + // parameters, the BR/EDR Controller shall send an HCI_QoS_Setup_Complete event to the Host, and + // the event may also be generated on the remote side if there was LMP negotiation. + if (IsEventUnmasked(EventCode::QOS_SETUP_COMPLETE)) { + uint32_t selected_token_rate = + token_rate == 0 || token_rate == 0xffffffff ? 100000 /* Ko/s */ : token_rate; + uint32_t selected_peak_bandwidth = peak_bandwidth == 0 || peak_bandwidth == 0xffffffff + ? 200000 /* Ko/s */ + : peak_bandwidth; + uint32_t selected_latency = latency == 0 || latency == 0xffffffff ? 50000 /* us */ : latency; + uint32_t selected_delay_variation = delay_variation == 0 || delay_variation == 0xffffffff + ? 10000 /* us */ + : delay_variation; + ScheduleTask(kNoDelayMs, [=, this]() { + send_event_(bluetooth::hci::QosSetupCompleteBuilder::Create( + ErrorCode::SUCCESS, connection_handle, bluetooth::hci::ServiceType(service_type), + selected_token_rate /* Token_Rate */, selected_peak_bandwidth /* Peak_Bandwidth */, + selected_latency /* Latency */, selected_delay_variation /* Delay_Variation */)); + }); } - // Create local connection context. - ScoConnectionParameters connection_parameters = { - request.GetTransmitBandwidth(), request.GetReceiveBandwidth(), - request.GetMaxLatency(), request.GetVoiceSetting(), - request.GetRetransmissionEffort(), request.GetPacketType()}; + // TODO: Implement LMP negotiation with peer. + // Right now we assume no LMP negotiation takes place. + return ErrorCode::SUCCESS; +} - 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); +// HCI Role Discovery command (Vol 4, Part E § 7.2.7). +ErrorCode BrEdrController::RoleDiscovery(uint16_t connection_handle, bluetooth::hci::Role* role) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; + } - // 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)); + *role = connections_.GetAclConnection(connection_handle).GetRole(); + return ErrorCode::SUCCESS; } -void BrEdrController::IncomingScoConnectionResponse(model::packets::LinkLayerPacketView incoming) { - Address address = incoming.GetSourceAddress(); - auto response = model::packets::ScoConnectionResponseView::Create(incoming); - ASSERT(response.IsValid()); - auto status = ErrorCode(response.GetStatus()); - auto sco_connection_handle = connections_.GetScoConnectionHandle(address); - bool is_legacy = connections_.IsLegacyScoConnection(address); - - if (!sco_connection_handle.has_value()) { - INFO(id_, "Received spurious eSCO connection response from {}", address); - return; +// HCI Switch Role command (Vol 4, Part E § 7.2.8). +ErrorCode BrEdrController::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. + auto connection_handle = connections_.GetAclConnectionHandle(bd_addr); + if (!connection_handle.has_value()) { + INFO(id_, "unknown connection address {}", bd_addr); + return ErrorCode::UNKNOWN_CONNECTION; } - INFO(id_, "Received eSCO connection response with status 0x{:02x} from {}", - static_cast(status), incoming.GetSourceAddress()); + AclConnection& connection = connections_.GetAclConnection(*connection_handle); - if (status == ErrorCode::SUCCESS) { - bool extended = response.GetExtended(); - ScoLinkParameters link_parameters = { - response.GetTransmissionInterval(), - response.GetRetransmissionWindow(), - response.GetRxPacketLength(), - response.GetTxPacketLength(), - response.GetAirMode(), - extended, - }; + // 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_.GetScoConnectionHandle(bd_addr).has_value()) { + INFO(id_, + "role switch rejected because an Sco link is opened with" + " the target device"); + return ErrorCode::COMMAND_DISALLOWED; + } - connections_.AcceptPendingScoConnection(address, link_parameters, [this, address] { - return BrEdrController::StartScoStream(address); - }); + // 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 (is_legacy) { - send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( - ErrorCode::SUCCESS, *sco_connection_handle, address, bluetooth::hci::LinkType::SCO, - bluetooth::hci::Enable::DISABLED)); - } else { - send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create( - ErrorCode::SUCCESS, *sco_connection_handle, 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)); - } + 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 BrEdrController::IncomingScoDisconnect(model::packets::LinkLayerPacketView incoming) { - Address address = incoming.GetSourceAddress(); - auto request = model::packets::ScoDisconnectView::Create(incoming); - ASSERT(request.IsValid()); - auto reason = request.GetReason(); - auto handle = connections_.GetScoConnectionHandle(address); +// HCI Read Link Policy Settings command (Vol 4, Part E § 7.2.9. +ErrorCode BrEdrController::ReadLinkPolicySettings(uint16_t connection_handle, + uint16_t* link_policy_settings) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; + } - INFO(id_, - "Received eSCO disconnection request with" - " reason 0x{:02x} from {}", - static_cast(reason), incoming.GetSourceAddress()); + *link_policy_settings = connections_.GetAclConnection(connection_handle).GetLinkPolicySettings(); + return ErrorCode::SUCCESS; +} - if (handle.has_value()) { - connections_.Disconnect(*handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); - SendDisconnectionCompleteEvent(*handle, ErrorCode(reason)); +// HCI Write Link Policy Settings command (Vol 4, Part E § 7.2.10). +ErrorCode BrEdrController::WriteLinkPolicySettings(uint16_t connection_handle, + uint16_t link_policy_settings) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } -} -void BrEdrController::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()); + if (link_policy_settings > 7 /* Sniff + Hold + Role switch */) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } - ASSERT(link_manager_ingest_lmp(lm_.get(), reinterpret_cast(address.data()), - packet.data(), packet.size())); + connections_.GetAclConnection(connection_handle).SetLinkPolicySettings(link_policy_settings); + return ErrorCode::SUCCESS; } -void BrEdrController::HandleAcl(bluetooth::hci::AclView acl) { - uint16_t connection_handle = acl.GetHandle(); - auto pb_flag = acl.GetPacketBoundaryFlag(); - auto bc_flag = acl.GetBroadcastFlag(); +// HCI Read Default Link Policy Settings command (Vol 4, Part E § 7.2.11). +ErrorCode BrEdrController::ReadDefaultLinkPolicySettings( + uint16_t* default_link_policy_settings) const { + *default_link_policy_settings = default_link_policy_settings_; + return ErrorCode::SUCCESS; +} - // TODO: Support Broadcast_Flag value of BR/EDR broadcast. - if (bc_flag != bluetooth::hci::BroadcastFlag::POINT_TO_POINT) { - FATAL("Received ACL HCI packet with Broadcast_flag set to unsupported value {}", - static_cast(bc_flag)); +// HCI Write Default Link Policy Settings command (Vol 4, Part E § 7.2.12). +ErrorCode BrEdrController::WriteDefaultLinkPolicySettings(uint16_t default_link_policy_settings) { + if (default_link_policy_settings > 7 /* Sniff + Hold + Role switch */) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - if (connections_.HasAclHandle(connection_handle)) { - // Classic ACL connection. - auto& connection = connections_.GetAclConnection(connection_handle); - auto acl_payload = acl.GetPayload(); - auto acl_packet = model::packets::AclBuilder::Create( - connection.own_address, connection.address, static_cast(pb_flag), - static_cast(bc_flag), std::vector(acl_payload.begin(), acl_payload.end())); - SendLinkLayerPacket(std::move(acl_packet)); + default_link_policy_settings_ = default_link_policy_settings; + return ErrorCode::SUCCESS; +} - } else { - // ACL HCI packets received with an unknown or invalid Connection Handle - // are silently dropped. - DEBUG("Received ACL HCI packet with invalid ACL connection handle 0x{:x}", connection_handle); +// HCI Flow Specification command (Vol 4, Part E § 7.2.13). +ErrorCode BrEdrController::FlowSpecification(uint16_t connection_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_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } - // Send immediate acknowledgment for the ACL packet. - // We don't really have a transmission queue in the controller. - ScheduleTask(kNoDelayMs, [this, connection_handle]() { - send_event_(bluetooth::hci::NumberOfCompletedPacketsBuilder::Create( - {bluetooth::hci::CompletedPackets(connection_handle, 1)})); - }); -} - -void BrEdrController::IncomingPagePacket(model::packets::LinkLayerPacketView incoming) { - if (!page_scan_enable_) { - return; + if (flow_direction > 0x01 || service_type > 0x02) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - auto bd_addr = incoming.GetSourceAddress(); - auto page = model::packets::PageView::Create(incoming); - ASSERT(page.IsValid()); + // TODO: implement real logic + return ErrorCode::COMMAND_DISALLOWED; +} - // [HCI] 7.3.3 Set Event Filter command - // If the Auto_Accept_Flag is off and the Host has masked the - // HCI_Connection_Request event, the Controller shall reject the - // connection attempt. - if (!IsEventUnmasked(EventCode::CONNECTION_REQUEST)) { - INFO(id_, - "rejecting connection request from {} because the HCI_Connection_Request" - " event is masked by the Host", - bd_addr); - SendLinkLayerPacket(model::packets::PageRejectBuilder::Create( - GetAddress(), bd_addr, static_cast(ErrorCode::CONNECTION_TIMEOUT))); - return; +// HCI Sniff Subrating command (Vol 4, Part E § 7.2.14). +ErrorCode BrEdrController::SniffSubrating(uint16_t connection_handle, uint16_t max_latency, + uint16_t min_remote_timeout, uint16_t min_local_timeout) { + if (!connections_.HasAclHandle(connection_handle)) { + return ErrorCode::UNKNOWN_CONNECTION; } - // Cannot establish two BR-EDR connections with the same peer. - if (connections_.GetAclConnectionHandle(bd_addr).has_value()) { - return; + if (max_latency < 0x2 || max_latency > 0xfffe || min_remote_timeout > 0xfffe || + min_local_timeout > 0xfffe) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } - // Cannot establish multiple connections simultaneously. - if (page_scan_.has_value()) { - INFO(id_, "ignoring connection request from {}, already connecting to {}", bd_addr, - page_scan_->bd_addr); - return; - } + // TODO: generate HCI Sniff Subrating event to emulate sniff subrating negotiation. + return ErrorCode::SUCCESS; +} - INFO(id_, "processing connection request from {}", bd_addr); +// ============================================================================= +// Controller & Baseband commands (Vol 4, Part E § 7.3) +// ============================================================================= - page_scan_ = PageScan{ - .bd_addr = bd_addr, - .authentication_required = authentication_enable_ == AuthenticationEnable::REQUIRED, - .allow_role_switch = page.GetAllowRoleSwitch(), - }; +// HCI Reset command (Vol 4, Part E § 7.3.2). +void BrEdrController::Reset() { + // Explicitly Disconnect all existing links on reset. + // No Disconnection Complete event should be generated from the link + // disconnections, as only the HCI Command Complete event is expected for the + // HCI Reset command. + DisconnectAll(ErrorCode::REMOTE_USER_TERMINATED_CONNECTION); - send_event_(bluetooth::hci::ConnectionRequestBuilder::Create( - bd_addr, page.GetClassOfDevice(), bluetooth::hci::ConnectionRequestLinkType::ACL)); -} + // DisconnectAll does not close the local connection contexts. + connections_.Reset([this](TaskId task_id) { CancelScheduledTask(task_id); }); -void BrEdrController::IncomingPageRejectPacket(model::packets::LinkLayerPacketView incoming) { - auto bd_addr = incoming.GetSourceAddress(); - auto reject = model::packets::PageRejectView::Create(incoming); - ASSERT(reject.IsValid()); + host_supported_features_ = 0; + le_host_support_ = false; + secure_simple_pairing_host_support_ = false; + secure_connections_host_support_ = false; + 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; + page_scan_repetition_mode_ = PageScanRepetitionMode::R0; + oob_id_ = 1; + key_id_ = 1; + inquiry_mode_ = InquiryType::STANDARD; - 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; - } + bluetooth::hci::Lap general_iac; + general_iac.lap_ = 0x33; // 0x9E8B33 + current_iac_lap_list_.clear(); + current_iac_lap_list_.emplace_back(general_iac); - INFO(id_, "Received Page Reject packet from {}", bd_addr); page_ = {}; + page_scan_ = {}; + inquiry_ = {}; - if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { - send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( - static_cast(reject.GetReason()), 0, bd_addr, bluetooth::hci::LinkType::ACL, - bluetooth::hci::Enable::DISABLED)); - } + lm_.reset(link_manager_create(controller_ops_)); } -void BrEdrController::IncomingPageResponsePacket(model::packets::LinkLayerPacketView incoming) { - auto bd_addr = incoming.GetSourceAddress(); - auto response = model::packets::PageResponseView::Create(incoming); - ASSERT(response.IsValid()); +// HCI Write Local Name command (Vol 4, Part E § 7.3.11). +void BrEdrController::WriteLocalName(std::array const& local_name) { + local_name_ = local_name; +} - 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; - } +// HCI Read Scan Enable command (Vol 4, Part E § 7.3.17). +void BrEdrController::ReadScanEnable(bluetooth::hci::ScanEnable* scan_enable) { + *scan_enable = inquiry_scan_enable_ && page_scan_enable_ + ? bluetooth::hci::ScanEnable::INQUIRY_AND_PAGE_SCAN + : inquiry_scan_enable_ ? bluetooth::hci::ScanEnable::INQUIRY_SCAN_ONLY + : page_scan_enable_ ? bluetooth::hci::ScanEnable::PAGE_SCAN_ONLY + : bluetooth::hci::ScanEnable::NO_SCANS; +} - INFO(id_, "Received Page Response packet from {}", bd_addr); +// HCI Write Scan Enable command (Vol 4, Part E § 7.3.18). +void BrEdrController::WriteScanEnable(bluetooth::hci::ScanEnable scan_enable) { + inquiry_scan_enable_ = scan_enable == bluetooth::hci::ScanEnable::INQUIRY_AND_PAGE_SCAN || + scan_enable == bluetooth::hci::ScanEnable::INQUIRY_SCAN_ONLY; + page_scan_enable_ = scan_enable == bluetooth::hci::ScanEnable::INQUIRY_AND_PAGE_SCAN || + scan_enable == bluetooth::hci::ScanEnable::PAGE_SCAN_ONLY; +} - uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress()); +// HCI Write Extended Inquiry Response command (Vol 4, Part E § 7.3.56). +void BrEdrController::WriteExtendedInquiryResponse( + bool /*fec_required*/, std::array const& extended_inquiry_response) { + extended_inquiry_response_ = extended_inquiry_response; +} - bluetooth::hci::Role role = page_->allow_role_switch && response.GetTryRoleSwitch() - ? bluetooth::hci::Role::PERIPHERAL - : bluetooth::hci::Role::CENTRAL; +// HCI Read Local OOB Data command (Vol 4, Part E § 7.3.60). +void BrEdrController::ReadLocalOobData(std::array* c, std::array* r) { + *c = std::array({'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', + '0', static_cast((oob_id_ % 0x10000) >> 8), + static_cast(oob_id_ % 0x100)}); - AclConnection& connection = connections_.GetAclConnection(connection_handle); - CheckExpiringConnection(connection_handle); - connection.SetLinkPolicySettings(default_link_policy_settings_); - connection.SetRole(role); - page_ = {}; + *r = std::array({'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', + '0', static_cast((oob_id_ % 0x10000) >> 8), + static_cast(oob_id_ % 0x100)}); + oob_id_ += 1; +} - ASSERT(link_manager_add_link(lm_.get(), reinterpret_cast(bd_addr.data()))); +// HCI Read Local OOB Extended Data command (Vol 4, Part E § 7.3.95). +void BrEdrController::ReadLocalOobExtendedData(std::array* c_192, + std::array* r_192, + std::array* c_256, + std::array* r_256) { + *c_192 = std::array({'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', '0', + '0', static_cast((oob_id_ % 0x10000) >> 8), + static_cast(oob_id_ % 0x100)}); - // 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)); - } + *r_192 = std::array({'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', '0', + '0', static_cast((oob_id_ % 0x10000) >> 8), + static_cast(oob_id_ % 0x100)}); - 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)); - } + *c_256 = std::array({'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', '0', + '0', static_cast((oob_id_ % 0x10000) >> 8), + static_cast(oob_id_ % 0x100)}); + + *r_256 = std::array({'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', '0', + '0', static_cast((oob_id_ % 0x10000) >> 8), + static_cast(oob_id_ % 0x100)}); + oob_id_ += 1; } -void BrEdrController::Tick() { - RunPendingTasks(); - Paging(); +// ============================================================================= +// Status parameters (Vol 4, Part E § 7.5) +// ============================================================================= - if (inquiry_timer_task_id_ != kInvalidTaskId) { - Inquiry(); +// HCI Read Rssi command (Vol 4, Part E § 7.5.4). +ErrorCode BrEdrController::ReadRssi(uint16_t connection_handle, int8_t* rssi) { + if (!connections_.HasAclHandle(connection_handle)) { + // Not documented: If the connection handle is not found, the Controller + // shall return the error code Unknown Connection Identifier (0x02). + return ErrorCode::UNKNOWN_CONNECTION; } - link_manager_tick(lm_.get()); -} -void BrEdrController::Close() { - DisconnectAll(ErrorCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF); + *rssi = connections_.GetAclConnection(connection_handle).GetRssi(); + return ErrorCode::SUCCESS; } -void BrEdrController::RegisterEventChannel( - const std::function)>& send_event) { - send_event_ = send_event; -} +// HCI Read Encryption Key Size command (Vol 4, Part E § 7.5.7). +ErrorCode BrEdrController::ReadEncryptionKeySize(uint16_t connection_handle, uint8_t* key_size) { + if (!connections_.HasAclHandle(connection_handle)) { + // Not documented: If the connection handle is not found, the Controller + // shall return the error code Unknown Connection Identifier (0x02). + return ErrorCode::UNKNOWN_CONNECTION; + } -void BrEdrController::RegisterAclChannel( - const std::function)>& send_acl) { - send_acl_ = send_acl; + // TODO: The Encryption Key Size should be specific to an ACL connection. + *key_size = 16; + return ErrorCode::SUCCESS; } -void BrEdrController::RegisterScoChannel( - const std::function)>& send_sco) { - send_sco_ = send_sco; -} +// ============================================================================= +// BR/EDR Commands +// ============================================================================= -void BrEdrController::RegisterRemoteChannel( - const std::function, Phy::Type, - int8_t)>& send_to_remote) { - send_to_remote_ = send_to_remote; +void BrEdrController::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 BrEdrController::ForwardToLm(bluetooth::hci::CommandView command) { - auto packet = command.bytes().bytes(); - ASSERT(link_manager_ingest_hci(lm_.get(), packet.data(), packet.size())); +void BrEdrController::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; + } } -std::vector const& BrEdrController::ReadCurrentIacLap() const { - return current_iac_lap_list_; +void BrEdrController::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 BrEdrController::WriteCurrentIacLap(std::vector 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); - } +void BrEdrController::SetLocalName(std::vector 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()); } -ErrorCode BrEdrController::AcceptConnectionRequest(const Address& bd_addr, bool try_role_switch) { - if (page_scan_.has_value() && page_scan_->bd_addr == 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); - }); +void BrEdrController::SetExtendedInquiryResponse( + std::vector 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()); +} - return ErrorCode::SUCCESS; +BrEdrController::BrEdrController(const Address& address, const ControllerProperties& properties, + uint32_t id) + : id_(id), address_(address), properties_(properties), lm_(nullptr, link_manager_destroy) { + controller_ops_ = { + .user_pointer = this, + .get_handle = + [](void* user, const uint8_t (*address)[6]) { + auto controller = static_cast(user); + + // Returns the connection handle but only for established + // BR-EDR connections. + return controller->connections_.GetAclConnectionHandle(Address(*address)) + .value_or(-1); + }, + + .get_address = + [](void* user, uint16_t handle, uint8_t (*result)[6]) { + auto controller = static_cast(user); + Address address = {}; + + if (controller->connections_.HasAclHandle(handle)) { + address = controller->connections_.GetAclConnection(handle).address; + } else if (controller->connections_.HasLeAclHandle(handle)) { + address = controller->connections_.GetLeAclConnection(handle) + .address.GetAddress(); + } + + std::copy(address.data(), address.data() + 6, + reinterpret_cast(result)); + }, + + .get_extended_features = + [](void* user, uint8_t features_page) { + auto controller = static_cast(user); + return controller->GetLmpFeatures(features_page); + }, + + .send_hci_event = + [](void* user, const uint8_t* data, uintptr_t len) { + auto controller = static_cast(user); + + auto event_code = static_cast(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(user); + + Address source = controller->GetAddress(); + Address dest(*to); + + controller->SendLinkLayerPacket(model::packets::LmpBuilder::Create( + source, dest, std::vector(data, data + len))); + }}; + + lm_.reset(link_manager_create(controller_ops_)); +} + +BrEdrController::~BrEdrController() {} + +void BrEdrController::SendLinkLayerPacket( + std::unique_ptr packet, int8_t tx_power) { + std::shared_ptr shared_packet = std::move(packet); + ScheduleTask(kNoDelayMs, [this, shared_packet, tx_power]() { + send_to_remote_(shared_packet, Phy::Type::BR_EDR, tx_power); + }); +} + +ErrorCode BrEdrController::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 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 BrEdrController::IncomingPacket(model::packets::LinkLayerPacketView incoming, int8_t rssi) { + ASSERT(incoming.IsValid()); + auto destination_address = incoming.GetDestinationAddress(); + auto source_address = incoming.GetSourceAddress(); + + // Accept broadcasts to address 00:00:00:00:00:00 but otherwise ignore the incoming + // packet if the destination address is not the local public address. + if (destination_address != Address::kEmpty && destination_address != address_) { + DEBUG(id_, "[LM] {} | Dropping {} packet not addressed to me {}->{}", address_, + PacketTypeText(incoming.GetType()), source_address, destination_address); + return; + } + + // Update link timeout for established ACL connections. + auto connection_handle = connections_.GetAclConnectionHandle(source_address); + if (connection_handle.has_value()) { + connections_.GetAclConnection(*connection_handle).ResetLinkTimer(); + } + + switch (incoming.GetType()) { + case model::packets::PacketType::ACL: + IncomingAclPacket(incoming, rssi); + break; + case model::packets::PacketType::SCO: + IncomingScoPacket(incoming); + break; + case model::packets::PacketType::DISCONNECT: + IncomingDisconnectPacket(incoming); + break; + case model::packets::PacketType::LMP: + IncomingLmpPacket(incoming); + break; + case model::packets::PacketType::INQUIRY: + IncomingInquiryPacket(incoming, rssi); + break; + case model::packets::PacketType::INQUIRY_RESPONSE: + IncomingInquiryResponsePacket(incoming); + break; + case model::packets::PacketType::PAGE: + 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; + default: + WARNING(id_, "Dropping unhandled packet of type {}", + model::packets::PacketTypeText(incoming.GetType())); + } +} + +void BrEdrController::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()); + + auto connection_handle = connections_.GetAclConnectionHandle(incoming.GetSourceAddress()); + if (!connection_handle.has_value()) { + INFO(id_, "Dropping packet since connection does not exist"); + return; + } + + // Update the RSSI for the local ACL connection. + auto& connection = connections_.GetAclConnection(*connection_handle); + connection.SetRssi(rssi); + + send_acl_(bluetooth::hci::AclBuilder::Create( + *connection_handle, packet_boundary_flag, broadcast_flag, + std::vector(acl_data.begin(), acl_data.end()))); +} + +void BrEdrController::IncomingScoPacket(model::packets::LinkLayerPacketView incoming) { + Address source = incoming.GetSourceAddress(); + auto sco_handle = connections_.GetScoConnectionHandle(source); + if (!sco_handle.has_value()) { + 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 sco_data_bytes(sco_data.begin(), sco_data.end()); + + INFO(id_, "Sco Packet [{}] {} -> {}", static_cast(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 BrEdrController::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 BrEdrController::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 BrEdrController::IncomingReadRemoteLmpFeatures(model::packets::LinkLayerPacketView incoming) { + SendLinkLayerPacket(model::packets::ReadRemoteLmpFeaturesResponseBuilder::Create( + incoming.GetDestinationAddress(), incoming.GetSourceAddress(), host_supported_features_)); +} + +void BrEdrController::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 BrEdrController::IncomingReadRemoteSupportedFeatures( + model::packets::LinkLayerPacketView incoming) { + SendLinkLayerPacket(model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create( + incoming.GetDestinationAddress(), incoming.GetSourceAddress(), + properties_.lmp_features[0])); +} + +void BrEdrController::IncomingReadRemoteSupportedFeaturesResponse( + model::packets::LinkLayerPacketView incoming) { + auto view = model::packets::ReadRemoteSupportedFeaturesResponseView::Create(incoming); + ASSERT(view.IsValid()); + Address source = incoming.GetSourceAddress(); + auto handle = connections_.GetAclConnectionHandle(source); + if (!handle.has_value()) { + 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 BrEdrController::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(ErrorCode::SUCCESS); + if (page_number >= properties_.lmp_features.size()) { + error_code = static_cast(ErrorCode::INVALID_LMP_OR_LL_PARAMETERS); + } + SendLinkLayerPacket(model::packets::ReadRemoteExtendedFeaturesResponseBuilder::Create( + incoming.GetDestinationAddress(), incoming.GetSourceAddress(), error_code, page_number, + GetMaxLmpFeaturesPageNumber(), GetLmpFeatures(page_number))); +} + +void BrEdrController::IncomingReadRemoteExtendedFeaturesResponse( + model::packets::LinkLayerPacketView incoming) { + auto view = model::packets::ReadRemoteExtendedFeaturesResponseView::Create(incoming); + ASSERT(view.IsValid()); + Address source = incoming.GetSourceAddress(); + auto handle = connections_.GetAclConnectionHandle(source); + if (!handle.has_value()) { + 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(view.GetStatus()), *handle, view.GetPageNumber(), + view.GetMaxPageNumber(), view.GetFeatures())); + } +} + +void BrEdrController::IncomingReadRemoteVersion(model::packets::LinkLayerPacketView incoming) { + SendLinkLayerPacket(model::packets::ReadRemoteVersionInformationResponseBuilder::Create( + incoming.GetDestinationAddress(), incoming.GetSourceAddress(), + static_cast(properties_.lmp_version), + static_cast(properties_.lmp_subversion), properties_.company_identifier)); +} + +void BrEdrController::IncomingReadRemoteVersionResponse( + model::packets::LinkLayerPacketView incoming) { + auto view = model::packets::ReadRemoteVersionInformationResponseView::Create(incoming); + ASSERT(view.IsValid()); + Address source = incoming.GetSourceAddress(); + + auto handle = connections_.GetAclConnectionHandle(source); + + if (!handle.has_value()) { + 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 BrEdrController::IncomingReadClockOffset(model::packets::LinkLayerPacketView incoming) { + SendLinkLayerPacket(model::packets::ReadClockOffsetResponseBuilder::Create( + incoming.GetDestinationAddress(), incoming.GetSourceAddress(), GetClockOffset())); +} + +void BrEdrController::IncomingReadClockOffsetResponse( + model::packets::LinkLayerPacketView incoming) { + auto view = model::packets::ReadClockOffsetResponseView::Create(incoming); + ASSERT(view.IsValid()); + Address source = incoming.GetSourceAddress(); + auto handle = connections_.GetAclConnectionHandle(source); + if (!handle.has_value()) { + 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())); } +} - // 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 = *connections_.GetScoConnectionHandle(bd_addr); - ScoLinkParameters link_parameters = {}; - ScoConnectionParameters connection_parameters = - connections_.GetScoConnectionParameters(bd_addr); +void BrEdrController::IncomingDisconnectPacket(model::packets::LinkLayerPacketView incoming) { + INFO(id_, "Disconnect Packet"); - if (!connections_.AcceptPendingScoConnection(bd_addr, connection_parameters, [this, bd_addr] { - return BrEdrController::StartScoStream(bd_addr); - })) { - connections_.CancelPendingScoConnection(bd_addr); - status = ErrorCode::SCO_INTERVAL_REJECTED; // TODO: proper status code - sco_handle = 0; - } else { - link_parameters = connections_.GetScoLinkParameters(bd_addr); - } + auto disconnect = model::packets::DisconnectView::Create(incoming); + ASSERT(disconnect.IsValid()); + + Address peer = incoming.GetSourceAddress(); + auto handle = connections_.GetAclConnectionHandle(peer); + if (!handle.has_value()) { + INFO(id_, "Discarding disconnect from a disconnected device {}", peer); + return; + } + + ASSERT_LOG(connections_.Disconnect(*handle, + [this](TaskId task_id) { CancelScheduledTask(task_id); }), + "GetHandle() returned invalid handle 0x{:x}", *handle); + + uint8_t reason = disconnect.GetReason(); + ASSERT(link_manager_remove_link(lm_.get(), reinterpret_cast(peer.data()))); + SendDisconnectionCompleteEvent(*handle, ErrorCode(reason)); +} + +void BrEdrController::IncomingInquiryPacket(model::packets::LinkLayerPacketView incoming, + uint8_t rssi) { + if (!inquiry_scan_enable_) { + return; + } + + 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(page_scan_repetition_mode_), + class_of_device_, GetClockOffset())); + } break; + case (model::packets::InquiryType::RSSI): { + SendLinkLayerPacket(model::packets::InquiryResponseWithRssiBuilder::Create( + GetAddress(), peer, static_cast(page_scan_repetition_mode_), + class_of_device_, GetClockOffset(), rssi)); + } break; + case (model::packets::InquiryType::EXTENDED): { + SendLinkLayerPacket(model::packets::ExtendedInquiryResponseBuilder::Create( + GetAddress(), peer, static_cast(page_scan_repetition_mode_), + class_of_device_, GetClockOffset(), rssi, extended_inquiry_response_)); + } break; + default: + WARNING(id_, "Unhandled Incoming Inquiry of type {}", static_cast(inquiry.GetType())); + return; + } + // TODO: Send an Inquiry Response Notification Event 7.7.74 +} + +void BrEdrController::IncomingInquiryResponsePacket(model::packets::LinkLayerPacketView incoming) { + auto basic_inquiry_response = model::packets::BasicInquiryResponseView::Create(incoming); + ASSERT(basic_inquiry_response.IsValid()); + std::vector 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 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 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( + 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(basic_inquiry_response.GetInquiryType())); + } +} + +void BrEdrController::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); - // 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)); + GetAddress(), address, (uint8_t)ErrorCode::SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED, 0, 0, + 0, 0, 0, 0)); + return; + } - // 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)); - }); - } + // Create local connection context. + ScoConnectionParameters connection_parameters = { + request.GetTransmitBandwidth(), request.GetReceiveBandwidth(), + request.GetMaxLatency(), request.GetVoiceSetting(), + request.GetRetransmissionEffort(), request.GetPacketType()}; - return ErrorCode::SUCCESS; + 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 BrEdrController::IncomingScoConnectionResponse(model::packets::LinkLayerPacketView incoming) { + Address address = incoming.GetSourceAddress(); + auto response = model::packets::ScoConnectionResponseView::Create(incoming); + ASSERT(response.IsValid()); + auto status = ErrorCode(response.GetStatus()); + auto sco_connection_handle = connections_.GetScoConnectionHandle(address); + bool is_legacy = connections_.IsLegacyScoConnection(address); + + if (!sco_connection_handle.has_value()) { + INFO(id_, "Received spurious eSCO connection response from {}", address); + return; } - INFO(id_, "No pending connection for {}", bd_addr); - return ErrorCode::UNKNOWN_CONNECTION; + INFO(id_, "Received eSCO connection response with status 0x{:02x} from {}", + static_cast(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 BrEdrController::StartScoStream(address); + }); + + if (is_legacy) { + send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( + ErrorCode::SUCCESS, *sco_connection_handle, address, bluetooth::hci::LinkType::SCO, + bluetooth::hci::Enable::DISABLED)); + } else { + send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create( + ErrorCode::SUCCESS, *sco_connection_handle, 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 BrEdrController::MakePeripheralConnection(const Address& bd_addr, bool try_role_switch) { - uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress()); +void BrEdrController::IncomingScoDisconnect(model::packets::LinkLayerPacketView incoming) { + Address address = incoming.GetSourceAddress(); + auto request = model::packets::ScoDisconnectView::Create(incoming); + ASSERT(request.IsValid()); + auto reason = request.GetReason(); + auto handle = connections_.GetScoConnectionHandle(address); - bluetooth::hci::Role role = try_role_switch && page_scan_->allow_role_switch - ? bluetooth::hci::Role::CENTRAL - : bluetooth::hci::Role::PERIPHERAL; + INFO(id_, + "Received eSCO disconnection request with" + " reason 0x{:02x} from {}", + static_cast(reason), incoming.GetSourceAddress()); - AclConnection& connection = connections_.GetAclConnection(connection_handle); - CheckExpiringConnection(connection_handle); - connection.SetLinkPolicySettings(default_link_policy_settings_); - connection.SetRole(role); + if (handle.has_value()) { + connections_.Disconnect(*handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); + SendDisconnectionCompleteEvent(*handle, ErrorCode(reason)); + } +} - ASSERT(link_manager_add_link(lm_.get(), reinterpret_cast(bd_addr.data()))); +void BrEdrController::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()); - // 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)); - } + ASSERT(link_manager_ingest_lmp(lm_.get(), reinterpret_cast(address.data()), + packet.data(), packet.size())); +} - 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 BrEdrController::HandleAcl(bluetooth::hci::AclView acl) { + uint16_t connection_handle = acl.GetHandle(); + auto pb_flag = acl.GetPacketBoundaryFlag(); + auto bc_flag = acl.GetBroadcastFlag(); - // 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 a unique HCI Connection Complete - // event is expected for both the HCI Create Connection and HCI Accept - // Connection Request commands. - page_ = {}; + // TODO: Support Broadcast_Flag value of BR/EDR broadcast. + if (bc_flag != bluetooth::hci::BroadcastFlag::POINT_TO_POINT) { + FATAL("Received ACL HCI packet with Broadcast_flag set to unsupported value {}", + static_cast(bc_flag)); } - // Reset the page scan state. - page_scan_ = {}; - - INFO(id_, "Sending page response to {}", bd_addr.ToString()); - SendLinkLayerPacket( - model::packets::PageResponseBuilder::Create(GetAddress(), bd_addr, try_role_switch)); -} + if (connections_.HasAclHandle(connection_handle)) { + // Classic ACL connection. + auto& connection = connections_.GetAclConnection(connection_handle); + auto acl_payload = acl.GetPayload(); + auto acl_packet = model::packets::AclBuilder::Create( + connection.own_address, connection.address, static_cast(pb_flag), + static_cast(bc_flag), std::vector(acl_payload.begin(), acl_payload.end())); + SendLinkLayerPacket(std::move(acl_packet)); -ErrorCode BrEdrController::RejectConnectionRequest(const Address& addr, uint8_t reason) { - if (!page_scan_.has_value() || page_scan_->bd_addr != addr) { - INFO(id_, "No pending connection for {}", addr); - return ErrorCode::UNKNOWN_CONNECTION; + } else { + // ACL HCI packets received with an unknown or invalid Connection Handle + // are silently dropped. + DEBUG("Received ACL HCI packet with invalid ACL connection handle 0x{:x}", connection_handle); } - ScheduleTask(kNoDelayMs, [this, addr, reason]() { RejectPeripheralConnection(addr, reason); }); - - return ErrorCode::SUCCESS; + // Send immediate acknowledgment for the ACL packet. + // We don't really have a transmission queue in the controller. + ScheduleTask(kNoDelayMs, [this, connection_handle]() { + send_event_(bluetooth::hci::NumberOfCompletedPacketsBuilder::Create( + {bluetooth::hci::CompletedPackets(connection_handle, 1)})); + }); } -void BrEdrController::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(reason), 0xeff, addr, bluetooth::hci::LinkType::ACL, - bluetooth::hci::Enable::DISABLED)); +void BrEdrController::IncomingPagePacket(model::packets::LinkLayerPacketView incoming) { + if (!page_scan_enable_) { + return; } -} -ErrorCode BrEdrController::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; + auto bd_addr = incoming.GetSourceAddress(); + auto page = model::packets::PageView::Create(incoming); + ASSERT(page.IsValid()); + + // [HCI] 7.3.3 Set Event Filter command + // If the Auto_Accept_Flag is off and the Host has masked the + // HCI_Connection_Request event, the Controller shall reject the + // connection attempt. + if (!IsEventUnmasked(EventCode::CONNECTION_REQUEST)) { + INFO(id_, + "rejecting connection request from {} because the HCI_Connection_Request" + " event is masked by the Host", + bd_addr); + SendLinkLayerPacket(model::packets::PageRejectBuilder::Create( + GetAddress(), bd_addr, static_cast(ErrorCode::CONNECTION_TIMEOUT))); + return; } - // Reject the command if a connection already exists - // for the selected peer address. + // Cannot establish two BR-EDR connections with the same peer. if (connections_.GetAclConnectionHandle(bd_addr).has_value()) { - INFO(id_, "Connection with {} already exists", bd_addr); - return ErrorCode::CONNECTION_ALREADY_EXISTS; + return; } - // Reject the command if a pending connection already exists - // for the selected peer address. - if (page_scan_.has_value() && page_scan_->bd_addr == bd_addr) { - INFO(id_, "Connection with {} is already being established", bd_addr); - return ErrorCode::CONNECTION_ALREADY_EXISTS; + // Cannot establish multiple connections simultaneously. + if (page_scan_.has_value()) { + INFO(id_, "ignoring connection request from {}, already connecting to {}", bd_addr, + page_scan_->bd_addr); + return; } - auto now = std::chrono::steady_clock::now(); - page_ = Page{ + INFO(id_, "processing connection request from {}", bd_addr); + + page_scan_ = PageScanState{ .bd_addr = bd_addr, - .allow_role_switch = allow_role_switch, - .next_page_event = now + kPageInterval, - .page_timeout = now + slots(page_timeout_), + .authentication_required = authentication_enable_ == AuthenticationEnable::REQUIRED, + .allow_role_switch = page.GetAllowRoleSwitch(), }; - return ErrorCode::SUCCESS; + send_event_(bluetooth::hci::ConnectionRequestBuilder::Create( + bd_addr, page.GetClassOfDevice(), bluetooth::hci::ConnectionRequestLinkType::ACL)); } -ErrorCode BrEdrController::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; - } +void BrEdrController::IncomingPageRejectPacket(model::packets::LinkLayerPacketView incoming) { + auto bd_addr = incoming.GetSourceAddress(); + auto reject = model::packets::PageRejectView::Create(incoming); + ASSERT(reject.IsValid()); - // 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)); - }); + 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_ = {}; - return ErrorCode::SUCCESS; -} -void BrEdrController::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)); - }); + if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { + send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( + static_cast(reject.GetReason()), 0, bd_addr, bluetooth::hci::LinkType::ACL, + bluetooth::hci::Enable::DISABLED)); } } -ErrorCode BrEdrController::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(host_reason))); +void BrEdrController::IncomingPageResponsePacket(model::packets::LinkLayerPacketView incoming) { + auto bd_addr = incoming.GetSourceAddress(); + auto response = model::packets::PageResponseView::Create(incoming); + ASSERT(response.IsValid()); - connections_.Disconnect(handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); - SendDisconnectionCompleteEvent(handle, controller_reason); - return ErrorCode::SUCCESS; + 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; } - if (connections_.HasAclHandle(handle)) { - auto connection = connections_.GetAclConnection(handle); - auto address = connection.address; - INFO(id_, "Disconnecting ACL connection with {}", connection.address); + INFO(id_, "Received Page Response packet from {}", bd_addr); - auto sco_handle = connections_.GetScoConnectionHandle(connection.address); - if (sco_handle.has_value()) { - SendLinkLayerPacket(model::packets::ScoDisconnectBuilder::Create( - connection.own_address, connection.address, static_cast(host_reason))); + uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress()); - connections_.Disconnect(*sco_handle, - [this](TaskId task_id) { CancelScheduledTask(task_id); }); - SendDisconnectionCompleteEvent(*sco_handle, controller_reason); - } + bluetooth::hci::Role role = page_->allow_role_switch && response.GetTryRoleSwitch() + ? bluetooth::hci::Role::PERIPHERAL + : bluetooth::hci::Role::CENTRAL; - SendLinkLayerPacket(model::packets::DisconnectBuilder::Create( - connection.own_address, connection.address, static_cast(host_reason))); + AclConnection& connection = connections_.GetAclConnection(connection_handle); + CheckExpiringConnection(connection_handle); + connection.SetLinkPolicySettings(default_link_policy_settings_); + connection.SetRole(role); + page_ = {}; - connections_.Disconnect(handle, [this](TaskId task_id) { CancelScheduledTask(task_id); }); - SendDisconnectionCompleteEvent(handle, controller_reason); + ASSERT(link_manager_add_link(lm_.get(), reinterpret_cast(bd_addr.data()))); - ASSERT(link_manager_remove_link(lm_.get(), reinterpret_cast(address.data()))); - return ErrorCode::SUCCESS; + // 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)); } - - return ErrorCode::UNKNOWN_CONNECTION; } -ErrorCode BrEdrController::ReadRemoteVersionInformation(uint16_t connection_handle) { - if (connections_.HasAclHandle(connection_handle)) { - auto const& connection = connections_.GetAclConnection(connection_handle); - SendLinkLayerPacket(model::packets::ReadRemoteVersionInformationBuilder::Create( - connection.own_address, connection.address)); - return ErrorCode::SUCCESS; - } +void BrEdrController::Tick() { + RunPendingTasks(); + Paging(); + Inquiry(); + link_manager_tick(lm_.get()); +} - return ErrorCode::UNKNOWN_CONNECTION; +void BrEdrController::Close() { + DisconnectAll(ErrorCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF); } -ErrorCode BrEdrController::ChangeConnectionPacketType(uint16_t handle, uint16_t types) { - if (!connections_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } +void BrEdrController::RegisterEventChannel( + const std::function)>& send_event) { + send_event_ = send_event; +} - ScheduleTask(kNoDelayMs, [this, handle, types]() { - if (IsEventUnmasked(EventCode::CONNECTION_PACKET_TYPE_CHANGED)) { - send_event_(bluetooth::hci::ConnectionPacketTypeChangedBuilder::Create(ErrorCode::SUCCESS, - handle, types)); - } - }); +void BrEdrController::RegisterAclChannel( + const std::function)>& send_acl) { + send_acl_ = send_acl; +} - return ErrorCode::SUCCESS; +void BrEdrController::RegisterScoChannel( + const std::function)>& send_sco) { + send_sco_ = send_sco; } -// NOLINTNEXTLINE(readability-convert-member-functions-to-static) -ErrorCode BrEdrController::ChangeConnectionLinkKey(uint16_t handle) { - if (!connections_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } +void BrEdrController::RegisterRemoteChannel( + const std::function, Phy::Type, + int8_t)>& send_to_remote) { + send_to_remote_ = send_to_remote; +} - // TODO: implement real logic - return ErrorCode::COMMAND_DISALLOWED; +void BrEdrController::ForwardToLm(bluetooth::hci::CommandView command) { + auto packet = command.bytes().bytes(); + ASSERT(link_manager_ingest_hci(lm_.get(), packet.data(), packet.size())); } -// NOLINTNEXTLINE(readability-convert-member-functions-to-static) -ErrorCode BrEdrController::CentralLinkKey(uint8_t /* key_flag */) { - // TODO: implement real logic - return ErrorCode::COMMAND_DISALLOWED; +std::vector const& BrEdrController::ReadCurrentIacLap() const { + return current_iac_lap_list_; } -ErrorCode BrEdrController::HoldMode(uint16_t handle, uint16_t hold_mode_max_interval, - uint16_t hold_mode_min_interval) { - if (!connections_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } +void BrEdrController::WriteCurrentIacLap(std::vector iac_lap) { + current_iac_lap_list_.swap(iac_lap); - if (hold_mode_max_interval < hold_mode_min_interval) { - return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + // 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); } - - // TODO: implement real logic - return ErrorCode::COMMAND_DISALLOWED; } -ErrorCode BrEdrController::SniffMode(uint16_t handle, uint16_t sniff_max_interval, - uint16_t sniff_min_interval, uint16_t sniff_attempt, - uint16_t sniff_timeout) { - if (!connections_.HasAclHandle(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; - } +void BrEdrController::MakePeripheralConnection(const Address& bd_addr, bool try_role_switch) { + uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress()); - // TODO: implement real logic - return ErrorCode::COMMAND_DISALLOWED; -} + bluetooth::hci::Role role = try_role_switch && page_scan_->allow_role_switch + ? bluetooth::hci::Role::CENTRAL + : bluetooth::hci::Role::PERIPHERAL; -ErrorCode BrEdrController::ExitSniffMode(uint16_t handle) { - if (!connections_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } + AclConnection& connection = connections_.GetAclConnection(connection_handle); + CheckExpiringConnection(connection_handle); + connection.SetLinkPolicySettings(default_link_policy_settings_); + connection.SetRole(role); - // TODO: implement real logic - return ErrorCode::COMMAND_DISALLOWED; -} + ASSERT(link_manager_add_link(lm_.get(), reinterpret_cast(bd_addr.data()))); -ErrorCode BrEdrController::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_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; + // 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 (service_type > 0x02) { - return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + 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)); } - // TODO: implement real logic - return ErrorCode::COMMAND_DISALLOWED; -} - -ErrorCode BrEdrController::RoleDiscovery(uint16_t handle, bluetooth::hci::Role* role) { - if (!connections_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; + // 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 a unique HCI Connection Complete + // event is expected for both the HCI Create Connection and HCI Accept + // Connection Request commands. + page_ = {}; } - *role = connections_.GetAclConnection(handle).GetRole(); - return ErrorCode::SUCCESS; -} - -ErrorCode BrEdrController::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. - auto connection_handle = connections_.GetAclConnectionHandle(bd_addr); - if (!connection_handle.has_value()) { - INFO(id_, "unknown connection address {}", bd_addr); - return ErrorCode::UNKNOWN_CONNECTION; - } + // Reset the page scan state. + page_scan_ = {}; - AclConnection& connection = connections_.GetAclConnection(*connection_handle); + INFO(id_, "Sending page response to {}", bd_addr.ToString()); + SendLinkLayerPacket( + model::packets::PageResponseBuilder::Create(GetAddress(), bd_addr, try_role_switch)); +} - // 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_.GetScoConnectionHandle(bd_addr).has_value()) { - INFO(id_, - "role switch rejected because an Sco link is opened with" - " the target device"); - return ErrorCode::COMMAND_DISALLOWED; - } +void BrEdrController::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 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 (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { + send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( + static_cast(reason), 0xeff, addr, bluetooth::hci::LinkType::ACL, + bluetooth::hci::Enable::DISABLED)); } +} - 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)); +void BrEdrController::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)); }); } +} - return ErrorCode::SUCCESS; +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +ErrorCode BrEdrController::CentralLinkKey(uint8_t /* key_flag */) { + // TODO: implement real logic + return ErrorCode::COMMAND_DISALLOWED; } void BrEdrController::IncomingRoleSwitchRequest(model::packets::LinkLayerPacketView incoming) { @@ -1447,130 +2143,42 @@ void BrEdrController::IncomingRoleSwitchRequest(model::packets::LinkLayerPacketV ? 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 BrEdrController::IncomingRoleSwitchResponse(model::packets::LinkLayerPacketView incoming) { - auto bd_addr = incoming.GetSourceAddress(); - auto connection_handle = connections_.GetAclConnectionHandle(bd_addr); - auto switch_rsp = model::packets::RoleSwitchResponseView::Create(incoming); - ASSERT(switch_rsp.IsValid()); - - if (!connection_handle.has_value()) { - 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 BrEdrController::ReadLinkPolicySettings(uint16_t handle, uint16_t* settings) { - if (!connections_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } - - *settings = connections_.GetAclConnection(handle).GetLinkPolicySettings(); - return ErrorCode::SUCCESS; -} - -ErrorCode BrEdrController::WriteLinkPolicySettings(uint16_t handle, uint16_t settings) { - if (!connections_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } - - if (settings > 7 /* Sniff + Hold + Role switch */) { - return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - connections_.GetAclConnection(handle).SetLinkPolicySettings(settings); - return ErrorCode::SUCCESS; -} - -ErrorCode BrEdrController::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 BrEdrController::ReadDefaultLinkPolicySettings() const { - return default_link_policy_settings_; -} - -void BrEdrController::ReadLocalOobData() { - std::array c_array({'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', - '0', static_cast((oob_id_ % 0x10000) >> 8), - static_cast(oob_id_ % 0x100)}); - - std::array r_array({'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', - '0', static_cast((oob_id_ % 0x10000) >> 8), - static_cast(oob_id_ % 0x100)}); - - send_event_(bluetooth::hci::ReadLocalOobDataCompleteBuilder::Create(1, ErrorCode::SUCCESS, - c_array, r_array)); - oob_id_ += 1; -} - -void BrEdrController::ReadLocalOobExtendedData() { - std::array c_192_array({'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', - '0', '0', static_cast((oob_id_ % 0x10000) >> 8), - static_cast(oob_id_ % 0x100)}); + connection.SetRole(new_role); - std::array r_192_array({'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', - '0', '0', static_cast((oob_id_ % 0x10000) >> 8), - static_cast(oob_id_ % 0x100)}); + if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { + ScheduleTask(kNoDelayMs, [this, bd_addr, new_role]() { + send_event_( + bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, bd_addr, new_role)); + }); + } + } +} - std::array c_256_array({'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', - '0', '0', static_cast((oob_id_ % 0x10000) >> 8), - static_cast(oob_id_ % 0x100)}); +void BrEdrController::IncomingRoleSwitchResponse(model::packets::LinkLayerPacketView incoming) { + auto bd_addr = incoming.GetSourceAddress(); + auto connection_handle = connections_.GetAclConnectionHandle(bd_addr); + auto switch_rsp = model::packets::RoleSwitchResponseView::Create(incoming); + ASSERT(switch_rsp.IsValid()); - std::array r_256_array({'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', - '0', '0', static_cast((oob_id_ % 0x10000) >> 8), - static_cast(oob_id_ % 0x100)}); + if (!connection_handle.has_value()) { + INFO(id_, "ignoring Switch Response received on unknown connection"); + return; + } - send_event_(bluetooth::hci::ReadLocalOobExtendedDataCompleteBuilder::Create( - 1, ErrorCode::SUCCESS, c_192_array, r_192_array, c_256_array, r_256_array)); - oob_id_ += 1; -} + 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; -ErrorCode BrEdrController::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_.HasAclHandle(handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } + connection.SetRole(new_role); - if (flow_direction > 0x01 || service_type > 0x02) { - return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { + ScheduleTask(kNoDelayMs, [this, status, bd_addr, new_role]() { + send_event_(bluetooth::hci::RoleChangeBuilder::Create(status, bd_addr, new_role)); + }); } - - // TODO: implement real logic - return ErrorCode::COMMAND_DISALLOWED; } ErrorCode BrEdrController::WriteLinkSupervisionTimeout(uint16_t handle, uint16_t /* timeout */) { @@ -1597,62 +2205,6 @@ void BrEdrController::DisconnectAll(ErrorCode reason) { } } -void BrEdrController::Reset() { - // Explicitly Disconnect all existing links on reset. - // No Disconnection Complete event should be generated from the link - // disconnections, as only the HCI Command Complete event is expected for the - // HCI Reset command. - DisconnectAll(ErrorCode::REMOTE_USER_TERMINATED_CONNECTION); - - // DisconnectAll does not close the local connection contexts. - connections_.Reset([this](TaskId task_id) { CancelScheduledTask(task_id); }); - - host_supported_features_ = 0; - le_host_support_ = false; - secure_simple_pairing_host_support_ = false; - secure_connections_host_support_ = false; - 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; - page_scan_repetition_mode_ = PageScanRepetitionMode::R0; - oob_id_ = 1; - key_id_ = 1; - last_inquiry_ = steady_clock::now(); - inquiry_mode_ = InquiryType::STANDARD; - inquiry_lap_ = 0; - inquiry_max_responses_ = 0; - - bluetooth::hci::Lap general_iac; - general_iac.lap_ = 0x33; // 0x9E8B33 - current_iac_lap_list_.clear(); - current_iac_lap_list_.emplace_back(general_iac); - - page_ = {}; - page_scan_ = {}; - - if (inquiry_timer_task_id_ != kInvalidTaskId) { - CancelScheduledTask(inquiry_timer_task_id_); - inquiry_timer_task_id_ = kInvalidTaskId; - } - - lm_.reset(link_manager_create(controller_ops_)); -} - /// Drive the logic for the Page controller substate. void BrEdrController::Paging() { auto now = std::chrono::steady_clock::now(); @@ -1679,198 +2231,34 @@ void BrEdrController::Paging() { } } -void BrEdrController::StartInquiry(milliseconds timeout) { - inquiry_timer_task_id_ = - ScheduleTask(milliseconds(timeout), [this]() { BrEdrController::InquiryTimeout(); }); -} - -void BrEdrController::InquiryCancel() { - ASSERT(inquiry_timer_task_id_ != kInvalidTaskId); - CancelScheduledTask(inquiry_timer_task_id_); - inquiry_timer_task_id_ = kInvalidTaskId; -} - -void BrEdrController::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 BrEdrController::SetInquiryMode(uint8_t mode) { inquiry_mode_ = static_cast(mode); } -void BrEdrController::SetInquiryLAP(uint64_t lap) { inquiry_lap_ = lap; } - -void BrEdrController::SetInquiryMaxResponses(uint8_t max) { inquiry_max_responses_ = max; } - +/// Drive the logic for the Inquiry controller substate. void BrEdrController::Inquiry() { - steady_clock::time_point now = steady_clock::now(); - if (duration_cast(now - last_inquiry_) < milliseconds(2000)) { - return; - } - - SendLinkLayerPacket(model::packets::InquiryBuilder::Create(GetAddress(), Address::kEmpty, - inquiry_mode_, inquiry_lap_)); - last_inquiry_ = now; -} - -void BrEdrController::SetInquiryScanEnable(bool enable) { inquiry_scan_enable_ = enable; } - -void BrEdrController::SetPageScanEnable(bool enable) { page_scan_enable_ = enable; } - -void BrEdrController::SetPageTimeout(uint16_t page_timeout) { page_timeout_ = page_timeout; } - -ErrorCode BrEdrController::AddScoConnection(uint16_t connection_handle, uint16_t packet_type, - ScoDatapath datapath) { - if (!connections_.HasAclHandle(connection_handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } - - auto const& connection = connections_.GetAclConnection(connection_handle); - if (connections_.HasPendingScoConnection(connection.address)) { - return ErrorCode::COMMAND_DISALLOWED; - } - - INFO(id_, "Creating SCO connection with {}", connection.address); - - // 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(connection.address, connection_parameters, SCO_STATE_PENDING, - datapath, true); - - // Send SCO connection request to peer. - SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create( - GetAddress(), connection.address, 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 BrEdrController::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_.HasAclHandle(connection_handle)) { - return ErrorCode::UNKNOWN_CONNECTION; - } - - auto const& connection = connections_.GetAclConnection(connection_handle); - if (connections_.HasPendingScoConnection(connection.address)) { - // 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 {}", connection.address); - - // Save connection parameters. - ScoConnectionParameters connection_parameters = {transmit_bandwidth, receive_bandwidth, - max_latency, voice_setting, - retransmission_effort, packet_types}; - connections_.CreateScoConnection(connection.address, connection_parameters, SCO_STATE_PENDING, - datapath); + auto now = std::chrono::steady_clock::now(); - // Send eSCO connection request to peer. - SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create( - GetAddress(), connection.address, transmit_bandwidth, receive_bandwidth, max_latency, - voice_setting, retransmission_effort, packet_types, class_of_device_)); - return ErrorCode::SUCCESS; -} + if (inquiry_.has_value() && now >= inquiry_->inquiry_timeout) { + INFO("inquiry timeout triggered"); -ErrorCode BrEdrController::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 (IsEventUnmasked(EventCode::INQUIRY_COMPLETE)) { + send_event_(bluetooth::hci::InquiryCompleteBuilder::Create(ErrorCode::SUCCESS)); + } - if (!connections_.HasPendingScoConnection(bd_addr)) { - INFO(id_, "No pending eSCO connection for {}", bd_addr); - return ErrorCode::COMMAND_DISALLOWED; + inquiry_ = {}; + return; } - ErrorCode status = ErrorCode::SUCCESS; - uint16_t sco_handle = *connections_.GetScoConnectionHandle(bd_addr); - 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 BrEdrController::StartScoStream(bd_addr); - })) { - connections_.CancelPendingScoConnection(bd_addr); - status = ErrorCode::STATUS_UNKNOWN; // TODO: proper status code - sco_handle = 0; - } else { - link_parameters = connections_.GetScoLinkParameters(bd_addr); + // Send an Inquiry packet to the peer when an inquiry interval has passed. + if (inquiry_.has_value() && now >= inquiry_->next_inquiry_event) { + SendLinkLayerPacket(model::packets::InquiryBuilder::Create(GetAddress(), Address::kEmpty, + inquiry_mode_, inquiry_->lap)); + inquiry_->next_inquiry_event = now + kInquiryInterval; } - - // 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 BrEdrController::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 BrEdrController::SetPageTimeout(uint16_t page_timeout) { page_timeout_ = page_timeout; } void BrEdrController::CheckExpiringConnection(uint16_t handle) { if (!connections_.HasAclHandle(handle)) { diff --git a/model/controller/bredr_controller.h b/model/controller/bredr_controller.h index 36abee3..46c3e72 100644 --- a/model/controller/bredr_controller.h +++ b/model/controller/bredr_controller.h @@ -46,18 +46,13 @@ namespace rootcanal { using ::bluetooth::hci::Address; -using ::bluetooth::hci::AddressType; using ::bluetooth::hci::AuthenticationEnable; using ::bluetooth::hci::ErrorCode; -using ::bluetooth::hci::FilterAcceptListAddressType; using ::bluetooth::hci::OpCode; using ::bluetooth::hci::PageScanRepetitionMode; -using rootcanal::apcf::ApcfScanner; class BrEdrController { public: - static constexpr size_t kIrkSize = 16; - static constexpr size_t kLtkSize = 16; static constexpr size_t kLocalNameSize = 248; static constexpr size_t kExtendedInquiryResponseSize = 240; @@ -67,9 +62,6 @@ class BrEdrController { BrEdrController(const Address& address, const ControllerProperties& properties, uint32_t id = 0); ~BrEdrController(); - ErrorCode SendCommandToRemoteByAddress(OpCode opcode, pdl::packet::slice args, - const Address& own_address, const Address& peer_address); - ErrorCode SendCommandToRemoteByHandle(OpCode opcode, pdl::packet::slice args, uint16_t handle); ErrorCode SendScoToRemote(bluetooth::hci::ScoView sco_packet); void ForwardToLm(bluetooth::hci::CommandView command); @@ -77,32 +69,112 @@ class BrEdrController { std::vector const& ReadCurrentIacLap() const; void WriteCurrentIacLap(std::vector iac_lap); - ErrorCode AcceptConnectionRequest(const Address& addr, bool try_role_switch); - void MakePeripheralConnection(const Address& addr, bool try_role_switch); - ErrorCode RejectConnectionRequest(const Address& addr, uint8_t reason); - void RejectPeripheralConnection(const Address& addr, uint8_t reason); - - // HCI command Create Connection (Vol 4, Part E § 7.1.5). - ErrorCode CreateConnection(const Address& bd_addr, uint16_t packet_type, uint8_t page_scan_mode, - uint16_t clock_offset, uint8_t allow_role_switch); + // Link Control commands (Vol 4, Part E § 7.1). - // HCI command Disconnect (Vol 4, Part E § 7.1.6). - // \p host_reason is taken from the Disconnect command, and sent over - // to the remote as disconnect error. \p controller_reason is the code - // used in the DisconnectionComplete event. + ErrorCode Inquiry(uint8_t lap, uint8_t inquiry_length, uint8_t num_responses); + ErrorCode InquiryCancel(); + ErrorCode CreateConnection(Address bd_addr, uint16_t packet_type, + uint8_t page_scan_repetition_mode, uint16_t clock_offset, + uint8_t allow_role_switch); ErrorCode Disconnect( - uint16_t handle, ErrorCode host_reason, + uint16_t connection_handle, ErrorCode host_reason, ErrorCode controller_reason = ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST); + ErrorCode CreateConnectionCancel(Address bd_addr); + ErrorCode AcceptConnectionRequest(Address bd_addr, bool try_role_switch); + ErrorCode RejectConnectionRequest(Address bd_addr, uint8_t reason); + ErrorCode ChangeConnectionPacketType(uint16_t connection_handle, uint16_t packet_type); + ErrorCode ChangeConnectionLinkKey(uint16_t connection_handle); + ErrorCode RemoteNameRequest(Address bd_addr, uint8_t page_scan_repetition_mode, + uint16_t clock_offset); + ErrorCode ReadRemoteSupportedFeatures(uint16_t connection_handle); + ErrorCode ReadRemoteExtendedFeatures(uint16_t connection_handle, uint8_t page_number); + ErrorCode ReadRemoteVersionInformation(uint16_t connection_handle); + ErrorCode ReadClockOffset(uint16_t connection_handle); + ErrorCode AddScoConnection(uint16_t connection_handle, uint16_t packet_type); + ErrorCode 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); + ErrorCode 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); + ErrorCode RejectSynchronousConnection(Address bd_addr, uint16_t reason); + ErrorCode EnhancedSetupSynchronousConnection( + uint16_t connection_handle, uint32_t transmit_bandwidth, uint32_t receive_bandwidth, + bluetooth::hci::ScoCodingFormat transmit_coding_format, + bluetooth::hci::ScoCodingFormat receive_coding_format, uint16_t transmit_codec_frame_size, + uint16_t receive_codec_frame_size, uint32_t input_bandwidth, uint32_t output_bandwidth, + bluetooth::hci::ScoCodingFormat input_coding_format, + bluetooth::hci::ScoCodingFormat output_coding_format, uint16_t input_coded_data_size, + uint16_t output_coded_data_size, bluetooth::hci::ScoPcmDataFormat input_pcm_data_format, + bluetooth::hci::ScoPcmDataFormat output_pcm_data_format, + uint8_t input_pcm_sample_payload_msb_position, + uint8_t output_pcm_sample_payload_msb_position, + bluetooth::hci::ScoDataPath input_data_path, bluetooth::hci::ScoDataPath output_data_path, + uint8_t input_transport_unit_size, uint8_t output_transport_unit_size, + uint16_t max_latency, uint16_t packet_type, + bluetooth::hci::RetransmissionEffort retransmission_effort); + ErrorCode EnhancedAcceptSynchronousConnection( + Address bd_addr, uint32_t transmit_bandwidth, uint32_t receive_bandwidth, + bluetooth::hci::ScoCodingFormat transmit_coding_format, + bluetooth::hci::ScoCodingFormat receive_coding_format, uint16_t transmit_codec_frame_size, + uint16_t receive_codec_frame_size, uint32_t input_bandwidth, uint32_t output_bandwidth, + bluetooth::hci::ScoCodingFormat input_coding_format, + bluetooth::hci::ScoCodingFormat output_coding_format, uint16_t input_coded_data_size, + uint16_t output_coded_data_size, bluetooth::hci::ScoPcmDataFormat input_pcm_data_format, + bluetooth::hci::ScoPcmDataFormat output_pcm_data_format, + uint8_t input_pcm_sample_payload_msb_position, + uint8_t output_pcm_sample_payload_msb_position, + bluetooth::hci::ScoDataPath input_data_path, bluetooth::hci::ScoDataPath output_data_path, + uint8_t input_transport_unit_size, uint8_t output_transport_unit_size, + uint16_t max_latency, uint16_t packet_type, + bluetooth::hci::RetransmissionEffort retransmission_effort); + + // Link Policy commands (Vol 4, Part E § 7.2). + + ErrorCode HoldMode(uint16_t connection_handle, uint16_t hold_mode_max_interval, + uint16_t hold_mode_min_interval); + ErrorCode SniffMode(uint16_t connection_handle, uint16_t sniff_max_interval, + uint16_t sniff_min_interval, uint16_t sniff_attempt, uint16_t sniff_timeout); + ErrorCode ExitSniffMode(uint16_t connection_handle); + ErrorCode QosSetup(uint16_t connection_handle, uint8_t service_type, uint32_t token_rate, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); + ErrorCode RoleDiscovery(uint16_t connection_handle, bluetooth::hci::Role* role); + ErrorCode SwitchRole(Address bd_addr, bluetooth::hci::Role role); + ErrorCode ReadLinkPolicySettings(uint16_t connection_handle, uint16_t* link_policy_settings); + ErrorCode WriteLinkPolicySettings(uint16_t connection_handle, uint16_t link_policy_settings); + ErrorCode ReadDefaultLinkPolicySettings(uint16_t* default_link_policy_settings) const; + ErrorCode WriteDefaultLinkPolicySettings(uint16_t default_link_policy_settings); + ErrorCode FlowSpecification(uint16_t connection_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); + ErrorCode SniffSubrating(uint16_t connection_handle, uint16_t max_latency, + uint16_t min_remote_timeout, uint16_t min_local_timeout); - // HCI command Create Connection Cancel (Vol 4, Part E § 7.1.7). - ErrorCode CreateConnectionCancel(const Address& bd_addr); + // Controller & Baseband commands (Vol 4, Part E § 7.3). - // HCI command Read Remote Version Information (Vol 4, Part E § 7.1.23). - ErrorCode ReadRemoteVersionInformation(uint16_t connection_handle); + void SetEventMask(uint64_t event_mask) { event_mask_ = event_mask; } + void Reset(); + void WriteLocalName(std::array const& local_name); + void ReadScanEnable(bluetooth::hci::ScanEnable* scan_enable); + void WriteScanEnable(bluetooth::hci::ScanEnable scan_enable); + void WriteExtendedInquiryResponse(bool fec_required, + std::array const& extended_inquiry_response); + void ReadLocalOobData(std::array* c, std::array* r); + void ReadLocalOobExtendedData(std::array* c_192, std::array* r_192, + std::array* c_256, std::array* r_256); + + // Status parameters (Vol 4, Part E § 7.5). + + ErrorCode ReadRssi(uint16_t connection_handle, int8_t* rssi); + ErrorCode ReadEncryptionKeySize(uint16_t connection_handle, uint8_t* key_size); // Internal task scheduler. + // // This scheduler is driven by the tick function only, // hence the precision of the scheduler is within a tick period. + class Task; using TaskId = uint32_t; using TaskCallback = std::function; @@ -126,6 +198,8 @@ class BrEdrController { private: void SendDisconnectionCompleteEvent(uint16_t handle, ErrorCode reason); + void MakePeripheralConnection(const Address& addr, bool try_role_switch); + void RejectPeripheralConnection(const Address& addr, uint8_t reason); public: const Address& GetAddress() const; @@ -153,73 +227,24 @@ class BrEdrController { const std::function, Phy::Type, int8_t)>& send_to_remote); - void Reset(); void Paging(); + void Inquiry(); - void StartInquiry(std::chrono::milliseconds timeout); - void InquiryCancel(); void InquiryTimeout(); void SetInquiryMode(uint8_t mode); - void SetInquiryLAP(uint64_t lap); - void SetInquiryMaxResponses(uint8_t max); - void Inquiry(); - - bool GetInquiryScanEnable() const { return inquiry_scan_enable_; } - void SetInquiryScanEnable(bool enable); - - bool GetPageScanEnable() const { return page_scan_enable_; } - void SetPageScanEnable(bool enable); uint16_t GetPageTimeout() const { return page_timeout_; } void SetPageTimeout(uint16_t page_timeout); - ErrorCode ChangeConnectionPacketType(uint16_t handle, uint16_t types); - ErrorCode ChangeConnectionLinkKey(uint16_t handle); ErrorCode CentralLinkKey(uint8_t key_flag); - ErrorCode HoldMode(uint16_t handle, uint16_t hold_mode_max_interval, - uint16_t hold_mode_min_interval); - ErrorCode SniffMode(uint16_t handle, uint16_t sniff_max_interval, uint16_t sniff_min_interval, - uint16_t sniff_attempt, uint16_t sniff_timeout); - ErrorCode ExitSniffMode(uint16_t handle); - ErrorCode QosSetup(uint16_t handle, uint8_t service_type, uint32_t token_rate, - uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); - ErrorCode RoleDiscovery(uint16_t handle, bluetooth::hci::Role* role); - ErrorCode SwitchRole(Address bd_addr, bluetooth::hci::Role role); - ErrorCode ReadLinkPolicySettings(uint16_t handle, uint16_t* settings); - ErrorCode WriteLinkPolicySettings(uint16_t handle, uint16_t settings); - ErrorCode 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); ErrorCode WriteLinkSupervisionTimeout(uint16_t handle, uint16_t timeout); - ErrorCode WriteDefaultLinkPolicySettings(uint16_t settings); void CheckExpiringConnection(uint16_t handle); - uint16_t ReadDefaultLinkPolicySettings() const; - - void ReadLocalOobData(); - void ReadLocalOobExtendedData(); - - ErrorCode AddScoConnection(uint16_t connection_handle, uint16_t packet_type, - ScoDatapath datapath); - ErrorCode 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); - ErrorCode 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); - ErrorCode RejectSynchronousConnection(Address bd_addr, uint16_t reason); // Returns true if the specified ACL connection handle is valid. bool HasAclConnection(uint16_t connection_handle); void HandleAcl(bluetooth::hci::AclView acl); - // BR/EDR Commands - - // HCI Read Rssi command (Vol 4, Part E § 7.5.4). - ErrorCode ReadRssi(uint16_t connection_handle, int8_t* rssi); - protected: void SendLinkLayerPacket(std::unique_ptr packet, int8_t tx_power = 0); @@ -265,26 +290,14 @@ class BrEdrController { // NOLINTNEXTLINE(readability-convert-member-functions-to-static) uint32_t GetClockOffset() const { return 0; } - // TODO - // The Page Scan Repetition Mode should be specific to an ACL connection or - // a paging session. - PageScanRepetitionMode GetPageScanRepetitionMode() const { return page_scan_repetition_mode_; } - - // TODO - // The Encryption Key Size should be specific to an ACL connection. - uint8_t GetEncryptionKeySize() const { return 16; } void SetMinEncryptionKeySize(uint8_t min_encryption_key_size) { min_encryption_key_size_ = min_encryption_key_size; } bool GetScoFlowControlEnable() const { return sco_flow_control_enable_; } - AuthenticationEnable GetAuthenticationEnable() { return authentication_enable_; } - std::array const& GetLocalName() { return local_name_; } - uint16_t GetConnectionAcceptTimeout() const { return connection_accept_timeout_; } - uint16_t GetVoiceSetting() const { return voice_setting_; } uint32_t GetClassOfDevice() const { return class_of_device_; } @@ -295,24 +308,15 @@ class BrEdrController { } void SetLocalName(std::vector const& local_name); - void SetLocalName(std::array const& local_name); - - void SetExtendedInquiryResponse(std::array const& extended_inquiry_response); void SetExtendedInquiryResponse(std::vector const& extended_inquiry_response); - void SetClassOfDevice(uint32_t class_of_device) { class_of_device_ = class_of_device; } - void SetAuthenticationEnable(AuthenticationEnable enable) { authentication_enable_ = enable; } - void SetScoFlowControlEnable(bool enable) { sco_flow_control_enable_ = enable; } void SetVoiceSetting(uint16_t voice_setting) { voice_setting_ = voice_setting; } - void SetEventMask(uint64_t event_mask) { event_mask_ = event_mask; } void SetEventMaskPage2(uint64_t event_mask) { event_mask_page_2_ = event_mask; } - void SetLeHostSupport(bool enable); void SetSecureSimplePairingSupport(bool enable); void SetSecureConnectionsSupport(bool enable); - void SetConnectionAcceptTimeout(uint16_t timeout) { connection_accept_timeout_ = timeout; } TaskId StartScoStream(Address address); @@ -342,6 +346,9 @@ class BrEdrController { uint16_t inquiry_scan_interval_{0x1000}; uint16_t inquiry_scan_window_{0x0012}; + // Inquiry Mode (Vol 4, Part E § 6.5). + model::packets::InquiryType inquiry_mode_{model::packets::InquiryType::STANDARD}; + // Page Timeout (Vol 4, Part E § 6.6). uint16_t page_timeout_{0x2000}; @@ -415,7 +422,7 @@ class BrEdrController { struct ControllerOps controller_ops_; // Classic state. - struct Page { + struct PageState { Address bd_addr; uint8_t allow_role_switch; std::chrono::steady_clock::time_point next_page_event{}; @@ -424,9 +431,9 @@ class BrEdrController { // Page substate. // RootCanal will allow only one page request running at the same time. - std::optional page_; + std::optional page_; - struct PageScan { + struct PageScanState { Address bd_addr; bool authentication_required; uint8_t allow_role_switch; @@ -435,13 +442,19 @@ class BrEdrController { // Page scan substate. // Set when page scan is enabled and a valid page request is received. // Holds the state for the connection being established. - std::optional page_scan_; + std::optional page_scan_; + + // Inquiry substate. + struct InquiryState { + uint8_t lap; + uint8_t num_responses; + std::chrono::steady_clock::time_point next_inquiry_event{}; + std::chrono::steady_clock::time_point inquiry_timeout{}; + }; - std::chrono::steady_clock::time_point last_inquiry_; - model::packets::InquiryType inquiry_mode_{model::packets::InquiryType::STANDARD}; - TaskId inquiry_timer_task_id_ = kInvalidTaskId; - uint64_t inquiry_lap_{}; - uint8_t inquiry_max_responses_{}; + // Inquiry substate. + // RootCanal will allow only one inquiry request running at the same time. + std::optional inquiry_; public: // Type of scheduled tasks. diff --git a/model/controller/controller_properties.cc b/model/controller/controller_properties.cc index 8387444..b52acec 100644 --- a/model/controller/controller_properties.cc +++ b/model/controller/controller_properties.cc @@ -117,6 +117,7 @@ static constexpr uint64_t LlFeatures() { LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_CENTRAL, LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL, + LLFeaturesBits::CONNECTION_SUBRATING, }; uint64_t value = 0; @@ -441,8 +442,8 @@ static std::array SupportedCommands() { // OpCodeIndex::LE_SET_TRANSMIT_POWER_REPORTING_ENABLE, // OpCodeIndex::LE_TRANSMITTER_TEST_V4, // OpCodeIndex::LE_SET_DATA_RELATED_ADDRESS_CHANGES, - // OpCodeIndex::LE_SET_DEFAULT_SUBRATE, - // OpCodeIndex::LE_SUBRATE_REQUEST, + OpCodeIndex::LE_SET_DEFAULT_SUBRATE, + OpCodeIndex::LE_SUBRATE_REQUEST, }; std::array value{}; @@ -1717,9 +1718,9 @@ static std::vector ll_privacy_commands_ = { OpCodeIndex::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT, }; -// Commands enabled by the LL Connected Isochronous Stream feature bit. +// Commands enabled by the Connected Isochronous Stream feature bit. // Central and Peripheral support bits are enabled together. -static std::vector ll_connected_isochronous_stream_commands_ = { +static std::vector connected_isochronous_stream_commands_ = { OpCodeIndex::LE_SET_CIG_PARAMETERS, OpCodeIndex::LE_SET_CIG_PARAMETERS_TEST, OpCodeIndex::LE_CREATE_CIS, OpCodeIndex::LE_REMOVE_CIG, OpCodeIndex::LE_ACCEPT_CIS_REQUEST, OpCodeIndex::LE_REJECT_CIS_REQUEST, @@ -1727,6 +1728,13 @@ static std::vector ll_connected_isochronous_stream_commands_ = { OpCodeIndex::LE_REQUEST_PEER_SCA, }; +// Commands enabled by the Connection Subrating feature bit. +// Central and Peripheral support bits are enabled together. +static std::vector connection_subrating_commands_ = { + OpCodeIndex::LE_SET_DEFAULT_SUBRATE, + OpCodeIndex::LE_SUBRATE_REQUEST, +}; + static void SetLLFeatureBit(uint64_t& le_features, LLFeaturesBits bit, bool set) { if (set) { le_features |= static_cast(bit); @@ -1889,9 +1897,15 @@ ControllerProperties::ControllerProperties(rootcanal::configuration::Controller features.le_connected_isochronous_stream()); SetLLFeatureBit(le_features, LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL, features.le_connected_isochronous_stream()); - SetSupportedCommandBits(supported_commands, ll_connected_isochronous_stream_commands_, + SetSupportedCommandBits(supported_commands, connected_isochronous_stream_commands_, features.le_connected_isochronous_stream()); } + if (features.has_le_connection_subrating()) { + SetLLFeatureBit(le_features, LLFeaturesBits::CONNECTION_SUBRATING, + features.le_connected_isochronous_stream()); + SetSupportedCommandBits(supported_commands, connection_subrating_commands_, + features.le_connection_subrating()); + } } // Apply selected quirks. diff --git a/model/controller/dual_mode_controller.cc b/model/controller/dual_mode_controller.cc index 71fa169..e2e6f7a 100644 --- a/model/controller/dual_mode_controller.cc +++ b/model/controller/dual_mode_controller.cc @@ -392,13 +392,15 @@ void DualModeController::ReadRssi(CommandView command) { void DualModeController::ReadEncryptionKeySize(CommandView command) { auto command_view = bluetooth::hci::ReadEncryptionKeySizeView::Create(command); CHECK_PACKET_VIEW(command_view); + uint16_t connection_handle = command_view.GetConnectionHandle(); DEBUG(id_, "<< Read Encryption Key Size"); - DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle()); + DEBUG(id_, " connection_handle=0x{:x}", connection_handle); + uint8_t key_size = 0; + auto status = bredr_controller_.ReadEncryptionKeySize(connection_handle, &key_size); send_event_(bluetooth::hci::ReadEncryptionKeySizeCompleteBuilder::Create( - kNumCommandPackets, ErrorCode::SUCCESS, command_view.GetConnectionHandle(), - bredr_controller_.GetEncryptionKeySize())); + kNumCommandPackets, status, connection_handle, key_size)); } void DualModeController::HostBufferSize(CommandView command) { @@ -505,15 +507,14 @@ void DualModeController::ReadLocalExtendedFeatures(CommandView command) { void DualModeController::ReadRemoteExtendedFeatures(CommandView command) { auto command_view = bluetooth::hci::ReadRemoteExtendedFeaturesView::Create(command); CHECK_PACKET_VIEW(command_view); + uint16_t connection_handle = command_view.GetConnectionHandle(); + uint8_t page_number = command_view.GetPageNumber(); DEBUG(id_, "<< Read Remote Extended Features"); - DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle()); - DEBUG(id_, " page_number={}", command_view.GetPageNumber()); - - auto status = bredr_controller_.SendCommandToRemoteByHandle(OpCode::READ_REMOTE_EXTENDED_FEATURES, - command_view.bytes(), - command_view.GetConnectionHandle()); + DEBUG(id_, " connection_handle=0x{:x}", connection_handle); + DEBUG(id_, " page_number={}", page_number); + auto status = bredr_controller_.ReadRemoteExtendedFeatures(connection_handle, page_number); send_event_(bluetooth::hci::ReadRemoteExtendedFeaturesStatusBuilder::Create(status, kNumCommandPackets)); } @@ -534,14 +535,12 @@ void DualModeController::SwitchRole(CommandView command) { void DualModeController::ReadRemoteSupportedFeatures(CommandView command) { auto command_view = bluetooth::hci::ReadRemoteSupportedFeaturesView::Create(command); CHECK_PACKET_VIEW(command_view); + auto connection_handle = command_view.GetConnectionHandle(); DEBUG(id_, "<< Read Remote Supported Features"); - DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle()); - - auto status = bredr_controller_.SendCommandToRemoteByHandle( - OpCode::READ_REMOTE_SUPPORTED_FEATURES, command_view.bytes(), - command_view.GetConnectionHandle()); + DEBUG(id_, " connection_handle=0x{:x}", connection_handle); + auto status = bredr_controller_.ReadRemoteSupportedFeatures(connection_handle); send_event_(bluetooth::hci::ReadRemoteSupportedFeaturesStatusBuilder::Create(status, kNumCommandPackets)); } @@ -549,15 +548,12 @@ void DualModeController::ReadRemoteSupportedFeatures(CommandView command) { void DualModeController::ReadClockOffset(CommandView command) { auto command_view = bluetooth::hci::ReadClockOffsetView::Create(command); CHECK_PACKET_VIEW(command_view); + uint16_t connection_handle = command_view.GetConnectionHandle(); DEBUG(id_, "<< Read Clock Offset"); - DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle()); - - uint16_t handle = command_view.GetConnectionHandle(); - - auto status = bredr_controller_.SendCommandToRemoteByHandle(OpCode::READ_CLOCK_OFFSET, - command_view.bytes(), handle); + DEBUG(id_, " connection_handle=0x{:x}", connection_handle); + auto status = bredr_controller_.ReadClockOffset(connection_handle); send_event_(bluetooth::hci::ReadClockOffsetStatusBuilder::Create(status, kNumCommandPackets)); } @@ -571,8 +567,8 @@ void DualModeController::AddScoConnection(CommandView command) { DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle()); DEBUG(id_, " packet_type=0x{:x}", command_view.GetPacketType()); - auto status = bredr_controller_.AddScoConnection( - command_view.GetConnectionHandle(), command_view.GetPacketType(), ScoDatapath::NORMAL); + auto status = bredr_controller_.AddScoConnection(command_view.GetConnectionHandle(), + command_view.GetPacketType()); send_event_(bluetooth::hci::AddScoConnectionStatusBuilder::Create(status, kNumCommandPackets)); } @@ -617,265 +613,50 @@ void DualModeController::AcceptSynchronousConnection(CommandView command) { void DualModeController::EnhancedSetupSynchronousConnection(CommandView command) { auto command_view = bluetooth::hci::EnhancedSetupSynchronousConnectionView::Create(command); - auto status = ErrorCode::SUCCESS; CHECK_PACKET_VIEW(command_view); DEBUG(id_, "<< Enhanced Setup Synchronous Connection"); DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle()); DEBUG(id_, " packet_type=0x{:x}", command_view.GetPacketType()); - // The Host shall set the Transmit_Coding_Format and Receive_Coding_Formats - // to be equal. - auto transmit_coding_format = command_view.GetTransmitCodingFormat(); - auto receive_coding_format = command_view.GetReceiveCodingFormat(); - if (transmit_coding_format.coding_format_ != receive_coding_format.coding_format_ || - transmit_coding_format.company_id_ != receive_coding_format.company_id_ || - transmit_coding_format.vendor_specific_codec_id_ != - receive_coding_format.vendor_specific_codec_id_) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Transmit_Coding_Format " - "({}) and Receive_Coding_Format ({}) as they are not equal", - transmit_coding_format.ToString(), receive_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // The Host shall either set the Input_Bandwidth and Output_Bandwidth - // to be equal, or shall set one of them to be zero and the other non-zero. - auto input_bandwidth = command_view.GetInputBandwidth(); - auto output_bandwidth = command_view.GetOutputBandwidth(); - if (input_bandwidth != output_bandwidth && input_bandwidth != 0 && output_bandwidth != 0) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Input_Bandwidth ({})" - " and Output_Bandwidth ({}) as they are not equal and different from 0", - input_bandwidth, output_bandwidth); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // The Host shall set the Input_Coding_Format and Output_Coding_Format - // to be equal. - auto input_coding_format = command_view.GetInputCodingFormat(); - auto output_coding_format = command_view.GetOutputCodingFormat(); - if (input_coding_format.coding_format_ != output_coding_format.coding_format_ || - input_coding_format.company_id_ != output_coding_format.company_id_ || - input_coding_format.vendor_specific_codec_id_ != - output_coding_format.vendor_specific_codec_id_) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Input_Coding_Format ({})" - " and Output_Coding_Format ({}) as they are not equal", - input_coding_format.ToString(), output_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // Root-Canal does not implement audio data transport paths other than the - // default HCI transport - other transports will receive spoofed data - ScoDatapath datapath = ScoDatapath::NORMAL; - if (command_view.GetInputDataPath() != bluetooth::hci::ScoDataPath::HCI || - command_view.GetOutputDataPath() != bluetooth::hci::ScoDataPath::HCI) { - WARNING(id_, - "EnhancedSetupSynchronousConnection: Input_Data_Path ({})" - " and/or Output_Data_Path ({}) are not over HCI, so data will be " - "spoofed", - static_cast(command_view.GetInputDataPath()), - static_cast(command_view.GetOutputDataPath())); - datapath = ScoDatapath::SPOOFED; - } - - // Either both the Transmit_Coding_Format and Input_Coding_Format shall be - // “transparent” or neither shall be. If both are “transparent”, the - // Transmit_Bandwidth and the Input_Bandwidth shall be the same and the - // Controller shall not modify the data sent to the remote device. - auto transmit_bandwidth = command_view.GetTransmitBandwidth(); - auto receive_bandwidth = command_view.GetReceiveBandwidth(); - if (transmit_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - transmit_bandwidth != input_bandwidth) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Transmit_Bandwidth ({})" - " and Input_Bandwidth ({}) as they are not equal", - transmit_bandwidth, input_bandwidth); - INFO(id_, - "EnhancedSetupSynchronousConnection: the Transmit_Bandwidth and " - "Input_Bandwidth shall be equal when both Transmit_Coding_Format " - "and Input_Coding_Format are 'transparent'"); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - if ((transmit_coding_format.coding_format_ == - bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != - (input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Transmit_Coding_Format " - "({}) and Input_Coding_Format ({}) as they are incompatible", - transmit_coding_format.ToString(), input_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // Either both the Receive_Coding_Format and Output_Coding_Format shall - // be “transparent” or neither shall be. If both are “transparent”, the - // Receive_Bandwidth and the Output_Bandwidth shall be the same and the - // Controller shall not modify the data sent to the Host. - if (receive_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - receive_bandwidth != output_bandwidth) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Receive_Bandwidth ({})" - " and Output_Bandwidth ({}) as they are not equal", - receive_bandwidth, output_bandwidth); - INFO(id_, - "EnhancedSetupSynchronousConnection: the Receive_Bandwidth and " - "Output_Bandwidth shall be equal when both Receive_Coding_Format " - "and Output_Coding_Format are 'transparent'"); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - if ((receive_coding_format.coding_format_ == - bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != - (output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Receive_Coding_Format " - "({}) and Output_Coding_Format ({}) as they are incompatible", - receive_coding_format.ToString(), output_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - if (status == ErrorCode::SUCCESS) { - status = bredr_controller_.SetupSynchronousConnection( - command_view.GetConnectionHandle(), transmit_bandwidth, receive_bandwidth, - command_view.GetMaxLatency(), bredr_controller_.GetVoiceSetting(), - static_cast(command_view.GetRetransmissionEffort()), - command_view.GetPacketType(), datapath); - } - + auto status = bredr_controller_.EnhancedSetupSynchronousConnection( + command_view.GetConnectionHandle(), command_view.GetTransmitBandwidth(), + command_view.GetReceiveBandwidth(), command_view.GetTransmitCodingFormat(), + command_view.GetReceiveCodingFormat(), command_view.GetTransmitCodecFrameSize(), + command_view.GetReceiveCodecFrameSize(), command_view.GetInputBandwidth(), + command_view.GetOutputBandwidth(), command_view.GetInputCodingFormat(), + command_view.GetOutputCodingFormat(), command_view.GetInputCodedDataSize(), + command_view.GetOutputCodedDataSize(), command_view.GetInputPcmDataFormat(), + command_view.GetOutputPcmDataFormat(), command_view.GetInputPcmSamplePayloadMsbPosition(), + command_view.GetOutputPcmSamplePayloadMsbPosition(), command_view.GetInputDataPath(), + command_view.GetOutputDataPath(), command_view.GetInputTransportUnitSize(), + command_view.GetOutputTransportUnitSize(), command_view.GetMaxLatency(), + command_view.GetPacketType(), command_view.GetRetransmissionEffort()); send_event_(bluetooth::hci::EnhancedSetupSynchronousConnectionStatusBuilder::Create( status, kNumCommandPackets)); } void DualModeController::EnhancedAcceptSynchronousConnection(CommandView command) { auto command_view = bluetooth::hci::EnhancedAcceptSynchronousConnectionView::Create(command); - auto status = ErrorCode::SUCCESS; CHECK_PACKET_VIEW(command_view); DEBUG(id_, "<< Enhanced Accept Synchronous Connection"); DEBUG(id_, " bd_addr={}", command_view.GetBdAddr()); DEBUG(id_, " packet_type=0x{:x}", command_view.GetPacketType()); - // The Host shall set the Transmit_Coding_Format and Receive_Coding_Formats - // to be equal. - auto transmit_coding_format = command_view.GetTransmitCodingFormat(); - auto receive_coding_format = command_view.GetReceiveCodingFormat(); - if (transmit_coding_format.coding_format_ != receive_coding_format.coding_format_ || - transmit_coding_format.company_id_ != receive_coding_format.company_id_ || - transmit_coding_format.vendor_specific_codec_id_ != - receive_coding_format.vendor_specific_codec_id_) { - INFO(id_, - "EnhancedAcceptSynchronousConnection: rejected Transmit_Coding_Format " - "({})" - " and Receive_Coding_Format ({}) as they are not equal", - transmit_coding_format.ToString(), receive_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // The Host shall either set the Input_Bandwidth and Output_Bandwidth - // to be equal, or shall set one of them to be zero and the other non-zero. - auto input_bandwidth = command_view.GetInputBandwidth(); - auto output_bandwidth = command_view.GetOutputBandwidth(); - if (input_bandwidth != output_bandwidth && input_bandwidth != 0 && output_bandwidth != 0) { - INFO(id_, - "EnhancedAcceptSynchronousConnection: rejected Input_Bandwidth ({})" - " and Output_Bandwidth ({}) as they are not equal and different from 0", - input_bandwidth, output_bandwidth); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // The Host shall set the Input_Coding_Format and Output_Coding_Format - // to be equal. - auto input_coding_format = command_view.GetInputCodingFormat(); - auto output_coding_format = command_view.GetOutputCodingFormat(); - if (input_coding_format.coding_format_ != output_coding_format.coding_format_ || - input_coding_format.company_id_ != output_coding_format.company_id_ || - input_coding_format.vendor_specific_codec_id_ != - output_coding_format.vendor_specific_codec_id_) { - INFO(id_, - "EnhancedAcceptSynchronousConnection: rejected Input_Coding_Format ({})" - " and Output_Coding_Format ({}) as they are not equal", - input_coding_format.ToString(), output_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // Root-Canal does not implement audio data transport paths other than the - // default HCI transport. - if (command_view.GetInputDataPath() != bluetooth::hci::ScoDataPath::HCI || - command_view.GetOutputDataPath() != bluetooth::hci::ScoDataPath::HCI) { - INFO(id_, - "EnhancedSetupSynchronousConnection: Input_Data_Path ({})" - " and/or Output_Data_Path ({}) are not over HCI, so data will be " - "spoofed", - static_cast(command_view.GetInputDataPath()), - static_cast(command_view.GetOutputDataPath())); - } - - // Either both the Transmit_Coding_Format and Input_Coding_Format shall be - // “transparent” or neither shall be. If both are “transparent”, the - // Transmit_Bandwidth and the Input_Bandwidth shall be the same and the - // Controller shall not modify the data sent to the remote device. - auto transmit_bandwidth = command_view.GetTransmitBandwidth(); - auto receive_bandwidth = command_view.GetReceiveBandwidth(); - if (transmit_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - transmit_bandwidth != input_bandwidth) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Transmit_Bandwidth ({})" - " and Input_Bandwidth ({}) as they are not equal", - transmit_bandwidth, input_bandwidth); - INFO(id_, - "EnhancedSetupSynchronousConnection: the Transmit_Bandwidth and " - "Input_Bandwidth shall be equal when both Transmit_Coding_Format " - "and Input_Coding_Format are 'transparent'"); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - if ((transmit_coding_format.coding_format_ == - bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != - (input_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Transmit_Coding_Format " - "({}) and Input_Coding_Format ({}) as they are incompatible", - transmit_coding_format.ToString(), input_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - // Either both the Receive_Coding_Format and Output_Coding_Format shall - // be “transparent” or neither shall be. If both are “transparent”, the - // Receive_Bandwidth and the Output_Bandwidth shall be the same and the - // Controller shall not modify the data sent to the Host. - if (receive_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT && - receive_bandwidth != output_bandwidth) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Receive_Bandwidth ({})" - " and Output_Bandwidth ({}) as they are not equal", - receive_bandwidth, output_bandwidth); - INFO(id_, - "EnhancedSetupSynchronousConnection: the Receive_Bandwidth and " - "Output_Bandwidth shall be equal when both Receive_Coding_Format " - "and Output_Coding_Format are 'transparent'"); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - if ((receive_coding_format.coding_format_ == - bluetooth::hci::ScoCodingFormatValues::TRANSPARENT) != - (output_coding_format.coding_format_ == bluetooth::hci::ScoCodingFormatValues::TRANSPARENT)) { - INFO(id_, - "EnhancedSetupSynchronousConnection: rejected Receive_Coding_Format " - "({}) and Output_Coding_Format ({}) as they are incompatible", - receive_coding_format.ToString(), output_coding_format.ToString()); - status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; - } - - if (status == ErrorCode::SUCCESS) { - status = bredr_controller_.AcceptSynchronousConnection( - command_view.GetBdAddr(), transmit_bandwidth, receive_bandwidth, - command_view.GetMaxLatency(), bredr_controller_.GetVoiceSetting(), - static_cast(command_view.GetRetransmissionEffort()), - command_view.GetPacketType()); - } - + auto status = bredr_controller_.EnhancedAcceptSynchronousConnection( + command_view.GetBdAddr(), command_view.GetTransmitBandwidth(), + command_view.GetReceiveBandwidth(), command_view.GetTransmitCodingFormat(), + command_view.GetReceiveCodingFormat(), command_view.GetTransmitCodecFrameSize(), + command_view.GetReceiveCodecFrameSize(), command_view.GetInputBandwidth(), + command_view.GetOutputBandwidth(), command_view.GetInputCodingFormat(), + command_view.GetOutputCodingFormat(), command_view.GetInputCodedDataSize(), + command_view.GetOutputCodedDataSize(), command_view.GetInputPcmDataFormat(), + command_view.GetOutputPcmDataFormat(), command_view.GetInputPcmSamplePayloadMsbPosition(), + command_view.GetOutputPcmSamplePayloadMsbPosition(), command_view.GetInputDataPath(), + command_view.GetOutputDataPath(), command_view.GetInputTransportUnitSize(), + command_view.GetOutputTransportUnitSize(), command_view.GetMaxLatency(), + command_view.GetPacketType(), command_view.GetRetransmissionEffort()); send_event_(bluetooth::hci::EnhancedAcceptSynchronousConnectionStatusBuilder::Create( status, kNumCommandPackets)); } @@ -943,7 +724,11 @@ void DualModeController::ReadLocalOobData(CommandView command) { DEBUG(id_, "<< Read Local Oob Data"); - bredr_controller_.ReadLocalOobData(); + std::array c{}; + std::array r{}; + bredr_controller_.ReadLocalOobData(&c, &r); + send_event_(bluetooth::hci::ReadLocalOobDataCompleteBuilder::Create(kNumCommandPackets, + ErrorCode::SUCCESS, c, r)); } void DualModeController::ReadLocalOobExtendedData(CommandView command) { @@ -951,7 +736,13 @@ void DualModeController::ReadLocalOobExtendedData(CommandView command) { DEBUG(id_, "<< Read Local Oob Extended Data"); - bredr_controller_.ReadLocalOobExtendedData(); + std::array c_192{}; + std::array r_192{}; + std::array c_256{}; + std::array r_256{}; + bredr_controller_.ReadLocalOobExtendedData(&c_192, &r_192, &c_256, &r_256); + send_event_(bluetooth::hci::ReadLocalOobExtendedDataCompleteBuilder::Create( + kNumCommandPackets, ErrorCode::SUCCESS, c_192, r_192, c_256, r_256)); } void DualModeController::SetMinEncryptionKeySize(CommandView command) { @@ -1288,9 +1079,10 @@ void DualModeController::ReadDefaultLinkPolicySettings(CommandView command) { DEBUG(id_, "<< Read Default Link Policy Settings"); - uint16_t settings = bredr_controller_.ReadDefaultLinkPolicySettings(); + uint16_t settings = 0; + auto status = bredr_controller_.ReadDefaultLinkPolicySettings(&settings); send_event_(bluetooth::hci::ReadDefaultLinkPolicySettingsCompleteBuilder::Create( - kNumCommandPackets, ErrorCode::SUCCESS, settings)); + kNumCommandPackets, status, settings)); } void DualModeController::WriteDefaultLinkPolicySettings(CommandView command) { @@ -1314,8 +1106,11 @@ void DualModeController::SniffSubrating(CommandView command) { DEBUG(id_, "<< Sniff Subrating"); DEBUG(id_, " connection_handle=0x{:x}", connection_handle); - send_event_(bluetooth::hci::SniffSubratingCompleteBuilder::Create( - kNumCommandPackets, ErrorCode::SUCCESS, connection_handle)); + auto status = bredr_controller_.SniffSubrating(connection_handle, command_view.GetMaxLatency(), + command_view.GetMinRemoteTimeout(), + command_view.GetMinLocalTimeout()); + send_event_(bluetooth::hci::SniffSubratingCompleteBuilder::Create(kNumCommandPackets, status, + connection_handle)); } void DualModeController::FlowSpecification(CommandView command) { @@ -1401,7 +1196,7 @@ void DualModeController::WriteLocalName(CommandView command) { DEBUG(id_, "<< Write Local Name"); - bredr_controller_.SetLocalName(command_view.GetLocalName()); + bredr_controller_.WriteLocalName(command_view.GetLocalName()); send_event_(bluetooth::hci::WriteLocalNameCompleteBuilder::Create(kNumCommandPackets, ErrorCode::SUCCESS)); } @@ -1412,7 +1207,9 @@ void DualModeController::WriteExtendedInquiryResponse(CommandView command) { DEBUG(id_, "<< Write Extended Inquiry Response"); - bredr_controller_.SetExtendedInquiryResponse(command_view.GetExtendedInquiryResponse()); + bredr_controller_.WriteExtendedInquiryResponse( + command_view.GetFecRequired() == bluetooth::hci::FecRequired::REQUIRED, + command_view.GetExtendedInquiryResponse()); send_event_(bluetooth::hci::WriteExtendedInquiryResponseCompleteBuilder::Create( kNumCommandPackets, ErrorCode::SUCCESS)); } @@ -1536,14 +1333,8 @@ void DualModeController::ReadScanEnable(CommandView command) { DEBUG(id_, "<< Read Scan Enable"); - bool inquiry_scan = bredr_controller_.GetInquiryScanEnable(); - bool page_scan = bredr_controller_.GetPageScanEnable(); - - bluetooth::hci::ScanEnable scan_enable = - inquiry_scan && page_scan ? bluetooth::hci::ScanEnable::INQUIRY_AND_PAGE_SCAN - : inquiry_scan ? bluetooth::hci::ScanEnable::INQUIRY_SCAN_ONLY - : page_scan ? bluetooth::hci::ScanEnable::PAGE_SCAN_ONLY - : bluetooth::hci::ScanEnable::NO_SCANS; + bluetooth::hci::ScanEnable scan_enable = bluetooth::hci::ScanEnable::NO_SCANS; + bredr_controller_.ReadScanEnable(&scan_enable); send_event_(bluetooth::hci::ReadScanEnableCompleteBuilder::Create( kNumCommandPackets, ErrorCode::SUCCESS, scan_enable)); @@ -1553,16 +1344,11 @@ void DualModeController::WriteScanEnable(CommandView command) { auto command_view = bluetooth::hci::WriteScanEnableView::Create(command); CHECK_PACKET_VIEW(command_view); bluetooth::hci::ScanEnable scan_enable = command_view.GetScanEnable(); - bool inquiry_scan = scan_enable == bluetooth::hci::ScanEnable::INQUIRY_AND_PAGE_SCAN || - scan_enable == bluetooth::hci::ScanEnable::INQUIRY_SCAN_ONLY; - bool page_scan = scan_enable == bluetooth::hci::ScanEnable::INQUIRY_AND_PAGE_SCAN || - scan_enable == bluetooth::hci::ScanEnable::PAGE_SCAN_ONLY; DEBUG(id_, "<< Write Scan Enable"); DEBUG(id_, " scan_enable={}", bluetooth::hci::ScanEnableText(scan_enable)); - bredr_controller_.SetInquiryScanEnable(inquiry_scan); - bredr_controller_.SetPageScanEnable(page_scan); + bredr_controller_.WriteScanEnable(scan_enable); send_event_(bluetooth::hci::WriteScanEnableCompleteBuilder::Create(kNumCommandPackets, ErrorCode::SUCCESS)); } @@ -1645,23 +1431,17 @@ void DualModeController::SetEventFilter(CommandView command) { void DualModeController::Inquiry(CommandView command) { auto command_view = bluetooth::hci::InquiryView::Create(command); CHECK_PACKET_VIEW(command_view); - auto max_responses = command_view.GetNumResponses(); - auto length = command_view.GetInquiryLength(); + auto lap = command_view.GetLap().lap_; + auto inquiry_length = command_view.GetInquiryLength(); + auto num_responses = command_view.GetNumResponses(); DEBUG(id_, "<< Inquiry"); - DEBUG(id_, " num_responses={}", max_responses); - DEBUG(id_, " inquiry_length={}", length); - - if (max_responses > 0xff || length < 1 || length > 0x30) { - send_event_(bluetooth::hci::InquiryStatusBuilder::Create( - ErrorCode::INVALID_HCI_COMMAND_PARAMETERS, kNumCommandPackets)); - return; - } - bredr_controller_.SetInquiryLAP(command_view.GetLap().lap_); - bredr_controller_.SetInquiryMaxResponses(max_responses); - bredr_controller_.StartInquiry(std::chrono::milliseconds(length * 1280)); + DEBUG(id_, " LAP={}", lap); + DEBUG(id_, " inquiry_length={}", inquiry_length); + DEBUG(id_, " num_responses={}", num_responses); - send_event_(bluetooth::hci::InquiryStatusBuilder::Create(ErrorCode::SUCCESS, kNumCommandPackets)); + auto status = bredr_controller_.Inquiry(lap, inquiry_length, num_responses); + send_event_(bluetooth::hci::InquiryStatusBuilder::Create(status, kNumCommandPackets)); } void DualModeController::InquiryCancel(CommandView command) { @@ -1670,9 +1450,8 @@ void DualModeController::InquiryCancel(CommandView command) { DEBUG(id_, "<< Inquiry Cancel"); - bredr_controller_.InquiryCancel(); - send_event_(bluetooth::hci::InquiryCancelCompleteBuilder::Create(kNumCommandPackets, - ErrorCode::SUCCESS)); + auto status = bredr_controller_.InquiryCancel(); + send_event_(bluetooth::hci::InquiryCancelCompleteBuilder::Create(kNumCommandPackets, status)); } void DualModeController::AcceptConnectionRequest(CommandView command) { @@ -1720,13 +1499,14 @@ void DualModeController::RemoteNameRequest(CommandView command) { auto command_view = bluetooth::hci::RemoteNameRequestView::Create(command); CHECK_PACKET_VIEW(command_view); Address bd_addr = command_view.GetBdAddr(); + uint8_t page_scan_repetition_mode = + static_cast(command_view.GetPageScanRepetitionMode()); DEBUG(id_, "<< Remote Name Request"); DEBUG(id_, " bd_addr={}", bd_addr); + DEBUG(id_, " page_scan_repetition_mode={}", page_scan_repetition_mode); - auto status = bredr_controller_.SendCommandToRemoteByAddress( - OpCode::REMOTE_NAME_REQUEST, command_view.bytes(), GetAddress(), bd_addr); - + auto status = bredr_controller_.RemoteNameRequest(bd_addr, page_scan_repetition_mode, 0); send_event_(bluetooth::hci::RemoteNameRequestStatusBuilder::Create(status, kNumCommandPackets)); } @@ -1819,6 +1599,33 @@ void DualModeController::LeReadBufferSizeV2(CommandView command) { kNumCommandPackets, ErrorCode::SUCCESS, le_buffer_size, iso_buffer_size)); } +void DualModeController::LeSetDefaultSubrate(CommandView command) { + auto command_view = bluetooth::hci::LeSetDefaultSubrateView::Create(command); + CHECK_PACKET_VIEW(command_view); + + DEBUG(id_, "<< LE Default Subrate"); + + auto status = le_controller_.LeSetDefaultSubrate( + command_view.GetSubrateMin(), command_view.GetSubrateMax(), command_view.GetMaxLatency(), + command_view.GetContinuationNumber(), command_view.GetSupervisionTimeout()); + send_event_( + bluetooth::hci::LeSetDefaultSubrateCompleteBuilder::Create(kNumCommandPackets, status)); +} + +void DualModeController::LeSubrateRequest(CommandView command) { + auto command_view = bluetooth::hci::LeSubrateRequestView::Create(command); + CHECK_PACKET_VIEW(command_view); + + DEBUG(id_, "<< LE Subrate Request"); + DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle()); + + auto status = le_controller_.LeSubrateRequest( + command_view.GetConnectionHandle(), command_view.GetSubrateMin(), + command_view.GetSubrateMax(), command_view.GetMaxLatency(), + command_view.GetContinuationNumber(), command_view.GetSupervisionTimeout()); + send_event_(bluetooth::hci::LeSubrateRequestStatusBuilder::Create(status, kNumCommandPackets)); +} + void DualModeController::LeSetAddressResolutionEnable(CommandView command) { auto command_view = bluetooth::hci::LeSetAddressResolutionEnableView::Create(command); CHECK_PACKET_VIEW(command_view); @@ -4348,9 +4155,8 @@ DualModeController::GetHciCommandHandlers() { //&DualModeController::LeTransmitterTestV4}, //{OpCode::LE_SET_DATA_RELATED_ADDRESS_CHANGES, //&DualModeController::LeSetDataRelatedAddressChanges}, - //{OpCode::LE_SET_DEFAULT_SUBRATE, - //&DualModeController::LeSetDefaultSubrate}, - //{OpCode::LE_SUBRATE_REQUEST, &DualModeController::LeSubrateRequest}, + {OpCode::LE_SET_DEFAULT_SUBRATE, &DualModeController::LeSetDefaultSubrate}, + {OpCode::LE_SUBRATE_REQUEST, &DualModeController::LeSubrateRequest}, //{OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V2, //&DualModeController::LeSetExtendedAdvertisingParametersV2}, //{OpCode::LE_SET_DECISION_DATA, &DualModeController::LeSetDecisionData}, diff --git a/model/controller/dual_mode_controller.h b/model/controller/dual_mode_controller.h index 78c5fc6..60de329 100644 --- a/model/controller/dual_mode_controller.h +++ b/model/controller/dual_mode_controller.h @@ -523,6 +523,10 @@ class DualModeController : public Device { // 7.8.115 void LeSetHostFeatureV1(CommandView command); + // 7.8.123 - 7.8.124 + void LeSetDefaultSubrate(CommandView command); + void LeSubrateRequest(CommandView command); + // Vendor-specific Commands void LeGetVendorCapabilities(CommandView command); void LeBatchScan(CommandView command); diff --git a/model/controller/le_acl_connection.cc b/model/controller/le_acl_connection.cc index 8a707a2..f0656a2 100644 --- a/model/controller/le_acl_connection.cc +++ b/model/controller/le_acl_connection.cc @@ -26,13 +26,15 @@ namespace rootcanal { LeAclConnection::LeAclConnection(uint16_t handle, AddressWithType address, AddressWithType own_address, AddressWithType resolved_address, bluetooth::hci::Role role, - LeAclConnectionParameters connection_parameters) + LeAclConnectionParameters connection_parameters, + LeAclSubrateParameters subrate_parameters) : handle(handle), address(address), own_address(own_address), resolved_address(resolved_address), role(role), parameters(connection_parameters), + subrate_parameters(subrate_parameters), last_packet_timestamp_(std::chrono::steady_clock::now()), timeout_(std::chrono::seconds(3)) {} diff --git a/model/controller/le_acl_connection.h b/model/controller/le_acl_connection.h index 3f8973e..c027f67 100644 --- a/model/controller/le_acl_connection.h +++ b/model/controller/le_acl_connection.h @@ -54,6 +54,16 @@ struct LeAclConnectionParameters { uint16_t conn_supervision_timeout{}; }; +/// Ranges for acceptable subrate parameters. +/// LE Default Subrate parameters (Vol 4, Part E § 7.8.123). +struct LeAclSubrateParameters { + uint16_t subrate_min{1}; + uint16_t subrate_max{1}; + uint16_t max_latency{0}; + uint16_t continuation_number{0}; + uint16_t supervision_timeout{0x0c80}; +}; + // Model the LE connection of a device to the controller. class LeAclConnection final { public: @@ -64,10 +74,11 @@ class LeAclConnection final { const bluetooth::hci::Role role; LeAclConnectionParameters parameters; + LeAclSubrateParameters subrate_parameters; LeAclConnection(uint16_t handle, AddressWithType address, AddressWithType own_address, AddressWithType resolved_address, bluetooth::hci::Role role, - LeAclConnectionParameters parameters); + LeAclConnectionParameters parameters, LeAclSubrateParameters subrate_parameters); ~LeAclConnection() = default; void Encrypt(); diff --git a/model/controller/le_controller.cc b/model/controller/le_controller.cc index c6da6a5..848ad99 100644 --- a/model/controller/le_controller.cc +++ b/model/controller/le_controller.cc @@ -1875,6 +1875,343 @@ ErrorCode LeController::LeExtendedCreateConnection( return ErrorCode::SUCCESS; } +// ============================================================================= +// LE Connection Subrating +// ============================================================================= + +// HCI LE Set Default Subrate command (Vol 4, Part E § 7.8.123). +ErrorCode LeController::LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, + uint16_t max_latency, uint16_t continuation_number, + uint16_t supervision_timeout) { + // Note: no explicit error code stated for invalid values but assuming + // Unsupported Feature or Parameter Value (0x11) error code based on similar commands. + + if (subrate_min < 0x0001 || subrate_min > 0x01f4) { + INFO(id_, + "subrate_min (0x{:04x}) is outside the range" + " of supported values (0x0001 - 0x01f4)", + subrate_min); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (subrate_max < 0x0001 || subrate_max > 0x01f4) { + INFO(id_, + "subrate_max (0x{:04x}) is outside the range" + " of supported values (0x0001 - 0x01f4)", + subrate_max); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (subrate_min > subrate_max) { + INFO(id_, "subrate_min (0x{:04x}) is larger than subrate_max (0x{:04x})", subrate_min, + subrate_max); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (max_latency > 0x01f3) { + INFO(id_, + "max_latency (0x{:04x}) is outside the range" + " of supported values (0x0000 - 0x01f3)", + max_latency); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (continuation_number > 0x01f3) { + INFO(id_, + "continuation_number (0x{:04x}) is outside the range" + " of supported values (0x0000 - 0x01f3)", + continuation_number); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (supervision_timeout < 0x000a || supervision_timeout > 0x0c80) { + INFO(id_, + "supervision_timeout (0x{:04x}) is outside the range" + " of supported values (0x000a - 0x0c80)", + supervision_timeout); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + default_subrate_parameters_ = LeAclSubrateParameters{ + .subrate_min = subrate_min, + .subrate_max = subrate_max, + .max_latency = max_latency, + .continuation_number = continuation_number, + .supervision_timeout = supervision_timeout, + }; + + return ErrorCode::SUCCESS; +} + +// HCI LE Subrate Request command (Vol 4, Part E § 7.8.124). +ErrorCode LeController::LeSubrateRequest(uint16_t connection_handle, uint16_t subrate_min, + uint16_t subrate_max, uint16_t max_latency, + uint16_t continuation_number, + uint16_t supervision_timeout) { + // If the Connection_Handle parameter does not identify a current ACL connection, the + // Controller shall return the error code Unknown Connection Identifier (0x02). + if (!connections_.HasLeAclHandle(connection_handle)) { + INFO(id_, "unknown connection_handle (0x{:06x})", connection_handle); + return ErrorCode::UNKNOWN_CONNECTION; + } + + auto& connection = connections_.GetLeAclConnection(connection_handle); + + // Note: no explicit error code stated for invalid values but assuming + // Unsupported Feature or Parameter Value (0x11) error code based on similar commands. + + if (subrate_min < 0x0001 || subrate_min > 0x01f4) { + INFO(id_, + "subrate_min (0x{:04x}) is outside the range" + " of supported values (0x0001 - 0x01f4)", + subrate_min); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (subrate_max < 0x0001 || subrate_max > 0x01f4) { + INFO(id_, + "subrate_max (0x{:04x}) is outside the range" + " of supported values (0x0001 - 0x01f4)", + subrate_max); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + // If the Host issues this command with Subrate_Max less than Subrate_Min, the + // Controller shall return the error code Invalid HCI Command Parameters (0x12). + if (subrate_min > subrate_max) { + INFO(id_, "subrate_min (0x{:04x}) is larger than subrate_max (0x{:04x})", subrate_min, + subrate_max); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + + if (max_latency > 0x01f3) { + INFO(id_, + "max_latency (0x{:04x}) is outside the range" + " of supported values (0x0000 - 0x01f3)", + max_latency); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (continuation_number > 0x01f3) { + INFO(id_, + "continuation_number (0x{:04x}) is outside the range" + " of supported values (0x0000 - 0x01f3)", + continuation_number); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + if (supervision_timeout < 0x000a || supervision_timeout > 0x0c80) { + INFO(id_, + "supervision_timeout (0x{:04x}) is outside the range" + " of supported values (0x000a - 0x0c80)", + supervision_timeout); + return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; + } + + // If the Host issues this command with parameters such that + // Subrate_Max × (Max_Latency + 1) is greater than 500 or the current connection interval + // × Subrate_Max × (Max_Latency + 1) is greater than or equal to half the + // Supervision_Timeout parameter, the Controller shall return the error code + // Invalid HCI Command Parameters (0x12). + if (subrate_max * (max_latency + 1) > 500) { + INFO(id_, "subrate_max (0x{:04x}) x (max_latency (0x{:04x}) + 1) is greater than 500", + subrate_max, max_latency); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + + if (connection.parameters.conn_interval * subrate_max * (max_latency + 1) >= + supervision_timeout / 2) { + INFO(id_, + "connInterval (0x{:04x}) x subrate_max (0x{:04x}) x (max_latency (0x{:04x}) + 1)" + " is greater than or equal to supervision_timeout (0x{:04x}) / 2", + connection.parameters.conn_interval, subrate_max, max_latency, supervision_timeout); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + + // If the Host issues this command with Continuation_Number greater than or equal to + // Subrate_Max, then the Controller shall return the error code Invalid HCI Command + // Parameters (0x12). + if (continuation_number >= subrate_max) { + INFO(id_, "continuation_number (0x{:04x}) is larger than subrate_max (0x{:04x})", + continuation_number, subrate_max); + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + + if (connection.role == bluetooth::hci::Role::CENTRAL) { + // If the Central's Host issues this command when the Connection Subrating + // (Host Support) bit is not set in the Peripheral's FeatureSet, the Controller + // shall return the error code Unsupported Remote Feature (0x1A). + // TODO: implement when the peripheral feature set is tracked in the ACL connection object. + + // If this command is issued on the Central, the following rules shall apply + // when the Controller initiates the Connection Subrate Update procedure + // (see [Vol 6] Part B, Section 5.1.19): + // - The Peripheral latency shall be less than or equal to Max_Latency. + // - The subrate factor shall be between Subrate_Min and Subrate_Max. + // - The continuation number shall be equal to the lesser of + // Continuation_Number and (subrate factor - 1). + // - The connection supervision timeout shall be equal to Supervision_Timeout. + + // As Central, it is allowed to directly send + // LL_SUBRATE_IND to update the parameters. + SendLeLinkLayerPacket(LlSubrateIndBuilder::Create( + connection.own_address.GetAddress(), connection.address.GetAddress(), + static_cast(ErrorCode::SUCCESS), subrate_max, + /* subrate_base_event */ 0, max_latency, continuation_number, supervision_timeout)); + + // Update the connection parameters. + connection.parameters.conn_subrate_factor = subrate_max; + connection.parameters.conn_continuation_number = continuation_number; + connection.parameters.conn_peripheral_latency = max_latency; + connection.parameters.conn_supervision_timeout = supervision_timeout; + + // If this command is issued on the Central, it also sets the acceptable parameters + // for requests from the Peripheral (see [Vol 6] Part B, Section 5.1.20). The acceptable + // parameters set by this command override those provided via the HCI_LE_Set_Default_Subrate + // command or any values set by previous uses of this command on the same connection. + connection.subrate_parameters = LeAclSubrateParameters{ + .subrate_min = subrate_min, + .subrate_max = subrate_max, + .max_latency = max_latency, + .continuation_number = continuation_number, + .supervision_timeout = supervision_timeout, + }; + + if (IsLeEventUnmasked(SubeventCode::LE_SUBRATE_CHANGE)) { + ScheduleTask(kNoDelayMs, [=, this]() { + send_event_(bluetooth::hci::LeSubrateChangeBuilder::Create( + ErrorCode::SUCCESS, connection_handle, subrate_max, max_latency, + continuation_number, supervision_timeout)); + }); + } + } else { + // Send LL_SUBRATE_REQ and wait for LL_SUBRATE_IND in return. + SendLeLinkLayerPacket(LlSubrateReqBuilder::Create( + connection.own_address.GetAddress(), connection.address.GetAddress(), subrate_min, + subrate_max, max_latency, continuation_number, supervision_timeout)); + } + + return ErrorCode::SUCCESS; +} + +void LeController::IncomingLlSubrateReq(LeAclConnection& connection, + model::packets::LinkLayerPacketView incoming) { + auto subrate_req = model::packets::LlSubrateReqView::Create(incoming); + ASSERT(subrate_req.IsValid()); + ASSERT(connection.role == bluetooth::hci::Role::CENTRAL); + + LeAclSubrateParameters subrate_parameters = connection.subrate_parameters; + uint16_t subrate_factor_min = subrate_req.GetSubrateFactorMin(); + uint16_t subrate_factor_max = subrate_req.GetSubrateFactorMax(); + uint16_t max_latency = subrate_req.GetMaxLatency(); + uint16_t continuation_number = subrate_req.GetContinuationNumber(); + uint16_t timeout = subrate_req.GetTimeout(); + + ErrorCode status = ErrorCode::SUCCESS; + + // Validate parameters according to the rules set in + // section 5.1.20. Connection Subrate Request procedure. + if (subrate_factor_max < subrate_parameters.subrate_min || + subrate_factor_min > subrate_parameters.subrate_max) { + INFO(id_, "rejecting LL_Subrate_Req because of incompatible subrate_factor requirement"); + status = ErrorCode::INVALID_LMP_OR_LL_PARAMETERS; + } + + if (max_latency > subrate_parameters.max_latency) { + INFO(id_, "rejecting LL_Subrate_Req because of incompatible max_latency requirement"); + status = ErrorCode::INVALID_LMP_OR_LL_PARAMETERS; + } + + if (timeout > subrate_parameters.supervision_timeout) { + INFO(id_, "rejecting LL_Subrate_Req because of incompatible timeout requirement"); + status = ErrorCode::INVALID_LMP_OR_LL_PARAMETERS; + } + + if (max_latency > subrate_parameters.max_latency) { + INFO(id_, "rejecting LL_Subrate_Req because of incompatible max_latency requirement"); + status = ErrorCode::INVALID_LMP_OR_LL_PARAMETERS; + } + + if (connection.parameters.conn_interval * subrate_factor_min * (max_latency + 1) * 2 < timeout) { + INFO(id_, "rejecting LL_Subrate_Req because of incompatible timeout requirement"); + status = ErrorCode::INVALID_LMP_OR_LL_PARAMETERS; + } + + if (status != ErrorCode::SUCCESS) { + SendLeLinkLayerPacket(LlSubrateIndBuilder::Create(connection.own_address.GetAddress(), + connection.address.GetAddress(), + static_cast(status), 0, 0, 0, 0, 0)); + return; + } + + // If the Central accepts the Peripheral’s request, then the new connSubrateFactor shall be + // between Subrate_Min_acc and Subrate_Max_acc and shall also be between SubrateFactorMin_req and + // SubrateFactorMax_req. + uint16_t subrate_factor = std::min(subrate_factor_max, subrate_parameters.subrate_max); + + // If the Central accepts the Peripheral’s request, then the new connContinuationNumber shall + // equal + // min(max(Continuation_Number_acc, ContinuationNumber_req), (new connSubrateFactor) - 1). + continuation_number = + std::min(std::max(continuation_number, subrate_parameters.continuation_number), + subrate_factor - 1); + + // If the Central accepts the Peripheral’s request, then the new connPeripheralLatency shall be + // less than or equal to min(Max_Latency_req, Max_Latency_acc), + uint16_t perihperal_latency = std::min(max_latency, subrate_parameters.max_latency); + + // If the Central accepts the Peripheral’s request, then the new connSupervisionTimeout shall + // equal min(Timeout_req, Supervision_Timeout_acc). + uint16_t supervision_timeout = std::min(timeout, subrate_parameters.supervision_timeout); + + // Update the local connection parameters. + connection.parameters.conn_subrate_factor = subrate_factor; + connection.parameters.conn_continuation_number = continuation_number; + connection.parameters.conn_peripheral_latency = perihperal_latency; + connection.parameters.conn_supervision_timeout = supervision_timeout; + + if (IsLeEventUnmasked(SubeventCode::LE_SUBRATE_CHANGE)) { + ScheduleTask(kNoDelayMs, [=, this]() { + send_event_(bluetooth::hci::LeSubrateChangeBuilder::Create( + ErrorCode::SUCCESS, connection.handle, subrate_factor, perihperal_latency, + continuation_number, supervision_timeout)); + }); + } + + SendLeLinkLayerPacket(LlSubrateIndBuilder::Create( + connection.own_address.GetAddress(), connection.address.GetAddress(), + static_cast(status), subrate_factor, /* subrate_base_event */ 0, + perihperal_latency, continuation_number, supervision_timeout)); +} + +void LeController::IncomingLlSubrateInd(LeAclConnection& connection, + model::packets::LinkLayerPacketView incoming) { + auto subrate_ind = model::packets::LlSubrateIndView::Create(incoming); + ASSERT(subrate_ind.IsValid()); + ASSERT(connection.role == bluetooth::hci::Role::PERIPHERAL); + + uint16_t subrate_factor = subrate_ind.GetSubrateFactor(); + uint16_t latency = subrate_ind.GetLatency(); + uint16_t continuation_number = subrate_ind.GetContinuationNumber(); + uint16_t timeout = subrate_ind.GetTimeout(); + ErrorCode status = static_cast(subrate_ind.GetStatus()); + + if (status == ErrorCode::SUCCESS) { + // Update the local connection parameters on success. + connection.parameters.conn_subrate_factor = subrate_factor; + connection.parameters.conn_continuation_number = continuation_number; + connection.parameters.conn_peripheral_latency = latency; + connection.parameters.conn_supervision_timeout = timeout; + } + + if (IsLeEventUnmasked(SubeventCode::LE_SUBRATE_CHANGE)) { + ScheduleTask(kNoDelayMs, [=, this]() { + send_event_(bluetooth::hci::LeSubrateChangeBuilder::Create( + status, connection.handle, subrate_factor, latency, continuation_number, timeout)); + }); + } +} + void LeController::SetSecureSimplePairingSupport(bool enable) { uint64_t bit = 0x1; secure_simple_pairing_host_support_ = enable; @@ -2106,6 +2443,12 @@ void LeController::IncomingPacket(model::packets::LinkLayerPacketView incoming, case model::packets::PacketType::LL_PHY_UPDATE_IND: IncomingLlPhyUpdateInd(connection, incoming); break; + case model::packets::PacketType::LL_SUBRATE_REQ: + IncomingLlSubrateReq(connection, incoming); + break; + case model::packets::PacketType::LL_SUBRATE_IND: + IncomingLlSubrateInd(connection, incoming); + break; default: WARNING(id_, "Dropping unhandled packet of type {}", model::packets::PacketTypeText(incoming.GetType())); @@ -3404,12 +3747,14 @@ uint16_t LeController::HandleLeConnection(AddressWithType address, AddressWithTy INFO(id_, "Creating LE connection with peer {}|{} and local address {}", address, resolved_address, own_address); + uint16_t handle = connections_.CreateLeConnection( address, resolved_address, own_address, role, LeAclConnectionParameters{.conn_interval = connection_interval, .conn_subrate_factor = 1, .conn_peripheral_latency = connection_latency, - .conn_supervision_timeout = supervision_timeout}); + .conn_supervision_timeout = supervision_timeout}, + default_subrate_parameters_); if (IsLeEventUnmasked(SubeventCode::LE_ENHANCED_CONNECTION_COMPLETE_V1)) { AddressWithType peer_resolved_address = resolved_address; @@ -3748,11 +4093,25 @@ void LeController::IncomingLeConnectionParameterUpdate( LeAclConnection& connection, model::packets::LinkLayerPacketView incoming) { auto update = model::packets::LeConnectionParameterUpdateView::Create(incoming); ASSERT(update.IsValid()); + ErrorCode status = static_cast(update.GetStatus()); + + if (status == ErrorCode::SUCCESS) { + // Update local connection parameters on success. + // If this command completes successfully and the connection interval has changed, then the + // subrating factor shall be set to 1 and the continuation number to 0. + connection.parameters = LeAclConnectionParameters{ + .conn_interval = update.GetInterval(), + .conn_subrate_factor = 1, + .conn_continuation_number = 0, + .conn_peripheral_latency = update.GetLatency(), + .conn_supervision_timeout = update.GetTimeout(), + }; + } if (IsLeEventUnmasked(SubeventCode::LE_CONNECTION_UPDATE_COMPLETE)) { send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create( - static_cast(update.GetStatus()), connection.handle, update.GetInterval(), - update.GetLatency(), update.GetTimeout())); + status, connection.handle, update.GetInterval(), update.GetLatency(), + update.GetTimeout())); } } @@ -4241,36 +4600,6 @@ ErrorCode LeController::ReadRemoteVersionInformation(uint16_t connection_handle) return ErrorCode::UNKNOWN_CONNECTION; } -void LeController::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_.HasLeAclHandle(handle)) { - status = ErrorCode::UNKNOWN_CONNECTION; - } - - auto const& connection = connections_.GetLeAclConnection(handle); - - 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( - connection.own_address.GetAddress(), connection.address.GetAddress(), - static_cast(ErrorCode::SUCCESS), interval, latency, supervision_timeout)); - - if (IsLeEventUnmasked(SubeventCode::LE_CONNECTION_UPDATE_COMPLETE)) { - send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create( - status, handle, interval, latency, supervision_timeout)); - } -} - ErrorCode LeController::LeConnectionUpdate(uint16_t handle, uint16_t interval_min, uint16_t interval_max, uint16_t latency, uint16_t supervision_timeout) { @@ -4288,6 +4617,7 @@ ErrorCode LeController::LeConnectionUpdate(uint16_t handle, uint16_t interval_mi static_cast(ErrorCode::SUCCESS), interval_max, latency, supervision_timeout)); if (IsLeEventUnmasked(SubeventCode::LE_CONNECTION_UPDATE_COMPLETE)) { + // TODO: should be delayed after the command status. send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create( ErrorCode::SUCCESS, handle, interval_max, latency, supervision_timeout)); } @@ -4303,20 +4633,49 @@ ErrorCode LeController::LeConnectionUpdate(uint16_t handle, uint16_t interval_mi } ErrorCode LeController::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) { + uint16_t connection_handle, uint16_t interval_min, uint16_t interval_max, + uint16_t supervision_timeout, uint16_t latency, uint16_t minimum_ce_length, + uint16_t maximum_ce_length) { if (!connections_.HasLeAclHandle(connection_handle)) { return ErrorCode::UNKNOWN_CONNECTION; } - if ((interval_min > interval_max) || (minimum_ce_length > maximum_ce_length)) { + auto& connection = connections_.GetLeAclConnection(connection_handle); + + 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)) { 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); - }); + if (minimum_ce_length > maximum_ce_length) { + return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; + } + + // Update local connection parameters. + // If this command completes successfully and the connection interval has changed, then the + // subrating factor shall be set to 1 and the continuation number to 0. + connection.parameters = LeAclConnectionParameters{ + .conn_interval = interval_min, + .conn_subrate_factor = 1, + .conn_continuation_number = 0, + .conn_peripheral_latency = latency, + .conn_supervision_timeout = supervision_timeout, + }; + + SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create( + connection.own_address.GetAddress(), connection.address.GetAddress(), + static_cast(ErrorCode::SUCCESS), interval_min, latency, supervision_timeout)); + + if (IsLeEventUnmasked(SubeventCode::LE_CONNECTION_UPDATE_COMPLETE)) { + // TODO: should be delayed after the command status. + send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create( + ErrorCode::SUCCESS, connection.handle, interval_min, latency, supervision_timeout)); + } + return ErrorCode::SUCCESS; } @@ -4327,12 +4686,10 @@ ErrorCode LeController::LeRemoteConnectionParameterRequestNegativeReply( } auto const& connection = connections_.GetLeAclConnection(connection_handle); - uint16_t interval = 0; - uint16_t latency = 0; - uint16_t timeout = 0; SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create( connection.own_address.GetAddress(), connection.address.GetAddress(), - static_cast(reason), interval, latency, timeout)); + static_cast(reason), 0, 0, 0)); + return ErrorCode::SUCCESS; } @@ -4460,6 +4817,7 @@ void LeController::Reset() { synchronized_ = {}; default_tx_phys_ = properties_.LeSupportedPhys(); default_rx_phys_ = properties_.LeSupportedPhys(); + default_subrate_parameters_ = LeAclSubrateParameters{}; ll_.reset(link_layer_create(controller_ops_)); } diff --git a/model/controller/le_controller.h b/model/controller/le_controller.h index bedc7e3..9ab8c97 100644 --- a/model/controller/le_controller.h +++ b/model/controller/le_controller.h @@ -151,8 +151,6 @@ class LeController { void LeScanning(); void LeSynchronization(); - void LeConnectionUpdateComplete(uint16_t handle, uint16_t interval_min, uint16_t interval_max, - uint16_t latency, uint16_t supervision_timeout); ErrorCode LeConnectionUpdate(uint16_t handle, uint16_t interval_min, uint16_t interval_max, uint16_t latency, uint16_t supervision_timeout); ErrorCode LeRemoteConnectionParameterRequestReply(uint16_t connection_handle, @@ -471,6 +469,15 @@ class LeController { // HCI LE Clear Periodic Advertiser List command (Vol 4, Part E § 7.8.72). ErrorCode LeClearPeriodicAdvertiserList(); + // HCI LE Set Default Subrate command (Vol 4, Part E § 7.8.123). + ErrorCode LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, + uint16_t continuation_number, uint16_t supervision_timeout); + + // HCI LE Subrate Request command (Vol 4, Part E § 7.8.124). + ErrorCode LeSubrateRequest(uint16_t connection_handle, uint16_t subrate_min, uint16_t subrate_max, + uint16_t max_latency, uint16_t continuation_number, + uint16_t supervision_timeout); + // LE APCF ErrorCode LeApcfEnable(bool apcf_enable); @@ -582,6 +589,11 @@ class LeController { void IncomingLlPhyUpdateInd(LeAclConnection& connection, model::packets::LinkLayerPacketView incoming); + void IncomingLlSubrateReq(LeAclConnection& connection, + model::packets::LinkLayerPacketView incoming); + void IncomingLlSubrateInd(LeAclConnection& connection, + model::packets::LinkLayerPacketView incoming); + public: bool IsEventUnmasked(bluetooth::hci::EventCode event) const; bool IsLeEventUnmasked(bluetooth::hci::SubeventCode subevent) const; @@ -661,6 +673,9 @@ class LeController { uint16_t le_suggested_max_tx_octets_{0x001b}; uint16_t le_suggested_max_tx_time_{0x0148}; + // LE Default Subrate parameters (Vol 4, Part E § 7.8.123). + LeAclSubrateParameters default_subrate_parameters_{}; + // Resolvable Private Address Timeout (Vol 4, Part E § 7.8.45). std::chrono::seconds resolvable_private_address_timeout_{0x0384}; diff --git a/packets/hci_packets.pdl b/packets/hci_packets.pdl index 4656e82..0b76434 100644 --- a/packets/hci_packets.pdl +++ b/packets/hci_packets.pdl @@ -1512,8 +1512,8 @@ packet EnhancedSetupSynchronousConnection : Command (op_code = ENHANCED_SETUP_SY output_coding_format : ScoCodingFormat, // Next two items // Size, in bits, of the sample or framed data - input_coded_data_bits : 16, - output_coded_data_bits : 16, + input_coded_data_size : 16, + output_coded_data_size : 16, input_pcm_data_format : ScoPcmDataFormat, output_pcm_data_format : ScoPcmDataFormat, // Next two items @@ -1527,8 +1527,8 @@ packet EnhancedSetupSynchronousConnection : Command (op_code = ENHANCED_SETUP_SY // [1, 255] The number of bits in each unit of data received from the Host // over the audio data transport. // [0] Not applicable (implied by the choice of audio data transport) - input_transport_unit_bits : 8, - output_transport_unit_bits : 8, + input_transport_unit_size : 8, + output_transport_unit_size : 8, // [0x0004, 0xFFFE]: in milliseconds // Upper limit represent the sum of the synchronous interval and the size // of the eSCO window, where the eSCO window is reserved slots plus the @@ -1568,8 +1568,8 @@ packet EnhancedAcceptSynchronousConnection : Command (op_code = ENHANCED_ACCEPT_ output_coding_format : ScoCodingFormat, // Next two items // Size, in bits, of the sample or framed data - input_coded_data_bits : 16, - output_coded_data_bits : 16, + input_coded_data_size : 16, + output_coded_data_size : 16, input_pcm_data_format : ScoPcmDataFormat, output_pcm_data_format : ScoPcmDataFormat, // Next two items @@ -1583,8 +1583,8 @@ packet EnhancedAcceptSynchronousConnection : Command (op_code = ENHANCED_ACCEPT_ // [1, 255] The number of bits in each unit of data received from the Host // over the audio data transport. // [0] Not applicable (implied by the choice of audio data transport) - input_transport_unit_bits : 8, - output_transport_unit_bits : 8, + input_transport_unit_size : 8, + output_transport_unit_size : 8, // [0x0004, 0xFFFE]: in milliseconds // Upper limit represent the sum of the synchronous interval and the size // of the eSCO window, where the eSCO window is reserved slots plus the @@ -1761,9 +1761,9 @@ packet FlowSpecificationStatus : CommandStatus (command_op_code = FLOW_SPECIFICA packet SniffSubrating : Command (op_code = SNIFF_SUBRATING) { connection_handle : 12, _reserved_ : 4, - maximum_latency : 16, // 0x0002-0xFFFE (1.25ms-40.9s) - minimum_remote_timeout : 16, // 0x0000-0xFFFE (0-40.9s) - minimum_local_timeout: 16, // 0x0000-0xFFFE (0-40.9s) + max_latency : 16, // 0x0002-0xFFFE (1.25ms-40.9s) + min_remote_timeout : 16, // 0x0000-0xFFFE (0-40.9s) + min_local_timeout: 16, // 0x0000-0xFFFE (0-40.9s) } packet SniffSubratingComplete : CommandComplete (command_op_code = SNIFF_SUBRATING) { @@ -4824,16 +4824,11 @@ packet LeSetDataRelatedAddressChangesComplete : CommandComplete (command_op_code } packet LeSetDefaultSubrate : Command (op_code = LE_SET_DEFAULT_SUBRATE) { - subrate_min : 9, - _reserved_ : 7, - subrate_max : 9, - _reserved_ : 7, - max_latency : 9, - _reserved_ : 7, - continuation_number : 9, - _reserved_ : 7, - supervision_timeout: 12, - _reserved_ : 4, + subrate_min : 16, + subrate_max : 16, + max_latency : 16, + continuation_number : 16, + supervision_timeout : 16, } packet LeSetDefaultSubrateComplete : CommandComplete (command_op_code = LE_SET_DEFAULT_SUBRATE) { @@ -4843,16 +4838,11 @@ packet LeSetDefaultSubrateComplete : CommandComplete (command_op_code = LE_SET_D packet LeSubrateRequest : Command (op_code = LE_SUBRATE_REQUEST) { connection_handle : 12, _reserved_ : 4, - subrate_min : 9, - _reserved_ : 7, - subrate_max : 9, - _reserved_ : 7, - max_latency : 9, - _reserved_ : 7, - continuation_number : 9, - _reserved_ : 7, - supervision_timeout: 12, - _reserved_ : 4, + subrate_min : 16, + subrate_max : 16, + max_latency : 16, + continuation_number : 16, + supervision_timeout : 16, } packet LeSubrateRequestStatus : CommandStatus (command_op_code = LE_SUBRATE_REQUEST) { diff --git a/packets/link_layer_packets.pdl b/packets/link_layer_packets.pdl index a4f763d..9a0cf32 100644 --- a/packets/link_layer_packets.pdl +++ b/packets/link_layer_packets.pdl @@ -57,6 +57,9 @@ enum PacketType : 8 { LL_PHY_REQ = 0x50, LL_PHY_RSP = 0x51, LL_PHY_UPDATE_IND = 0x52, + + LL_SUBRATE_REQ = 0x53, + LL_SUBRATE_IND = 0x54, } packet LinkLayerPacket { @@ -376,3 +379,21 @@ packet LlPhyUpdateInd : LinkLayerPacket (type = LL_PHY_UPDATE_IND) { phy_p_to_c: 8, instant: 16, } + +packet LlSubrateReq : LinkLayerPacket (type = LL_SUBRATE_REQ) { + subrate_factor_min: 16, + subrate_factor_max: 16, + max_latency: 16, + continuation_number: 16, + timeout: 16, +} + +packet LlSubrateInd : LinkLayerPacket (type = LL_SUBRATE_IND) { + // Not part of the LL PDU, added to emulate LL_REJECT_EXT_IND. + status: 8, + subrate_factor: 16, + subrate_base_event: 16, + latency: 16, + continuation_number: 16, + timeout: 16, +} diff --git a/proto/rootcanal/configuration.proto b/proto/rootcanal/configuration.proto index 0beb961..6d3ef61 100644 --- a/proto/rootcanal/configuration.proto +++ b/proto/rootcanal/configuration.proto @@ -37,6 +37,7 @@ message ControllerFeatures { // Enable the support for both LL Connected Isochronous Stream Central // and LL Connected Isochronous Stream Peripheral. optional bool le_connected_isochronous_stream = 6; + optional bool le_connection_subrating = 7; } message ControllerQuirks { diff --git a/rust/src/llcp/iso.rs b/rust/src/llcp/iso.rs index 9e59267..b4c757c 100644 --- a/rust/src/llcp/iso.rs +++ b/rust/src/llcp/iso.rs @@ -65,7 +65,7 @@ type microseconds = u32; type slots = u16; /// CIG configuration. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] struct CigConfig { // CIG parameters. iso_interval: slots, @@ -136,6 +136,21 @@ struct CisParameters { framed: bool, } +impl Default for CigConfig { + fn default() -> Self { + CigConfig { + iso_interval: 0, + sdu_interval_c_to_p: 0, + sdu_interval_p_to_c: 0, + ft_c_to_p: 0, + ft_p_to_c: 0, + framed: false, + // The CIG is initially configurable. + configurable: true, + } + } +} + impl CisParameters { fn new(cig_config: &CigConfig, cis_config: &CisConfig) -> CisParameters { let bn_c_to_p: u8 = cis_config @@ -1009,6 +1024,14 @@ impl IsoManager { return self.send_hci_event(command_status(hci::ErrorCode::CommandDisallowed)); } + // Tag the CIG as no longer configurable. + for cis_request in &cis_connection_requests { + self.cig_config + .get_mut(&cis_request.cig_id) + .unwrap() + .configurable = false; + } + // Update the pending CIS request list. cis_connection_requests.reverse(); self.cis_connection_requests = cis_connection_requests; diff --git a/rust/src/lmp/procedure/legacy_pairing.rs b/rust/src/lmp/procedure/legacy_pairing.rs index 4f7e87f..40f2664 100644 --- a/rust/src/lmp/procedure/legacy_pairing.rs +++ b/rust/src/lmp/procedure/legacy_pairing.rs @@ -40,7 +40,7 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> { let _ = ctx.receive_lmp_packet::().await; // Post pairing authentication - let link_key = [0; 16]; + let link_key = [1; 16]; let auth_result = authentication::send_challenge(ctx, 0, link_key).await; authentication::receive_challenge(ctx, link_key).await; @@ -74,7 +74,7 @@ pub async fn respond(ctx: &impl Context, _request: lmp::InRand) -> Result<(), () ctx.send_lmp_packet(lmp::CombKey { transaction_id: 0, random_number: [0; 16] }); // Post pairing authentication - let link_key = [0; 16]; + let link_key = [1; 16]; authentication::receive_challenge(ctx, link_key).await; let auth_result = authentication::send_challenge(ctx, 0, link_key).await; diff --git a/rust/src/lmp/procedure/secure_simple_pairing.rs b/rust/src/lmp/procedure/secure_simple_pairing.rs index 1fe6475..a028458 100644 --- a/rust/src/lmp/procedure/secure_simple_pairing.rs +++ b/rust/src/lmp/procedure/secure_simple_pairing.rs @@ -194,7 +194,10 @@ async fn send_commitment(ctx: &impl Context, confirm: lmp::SimplePairingConfirm) async fn user_confirmation_request(ctx: &impl Context) -> Result<(), ()> { ctx.send_hci_event(hci::UserConfirmationRequest { bd_addr: ctx.peer_address(), - numeric_value: 0, + // We are using a fixed numeric value here, but in a proper controller the + // value would be randomly generated. For the purpose of automated virtual + // testing it is not that important to do that. + numeric_value: 27, }); match ctx @@ -502,7 +505,7 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> { }); // Link Key Calculation - let link_key = [0; 16]; + let link_key = [1; 16]; let auth_result = authentication::send_challenge(ctx, 0, link_key).await; authentication::receive_challenge(ctx, link_key).await; @@ -730,7 +733,7 @@ pub async fn respond(ctx: &impl Context, request: lmp::IoCapabilityReq) -> Resul }); // Link Key Calculation - let link_key = [0; 16]; + let link_key = [1; 16]; authentication::receive_challenge(ctx, link_key).await; let auth_result = authentication::send_challenge(ctx, 0, link_key).await; diff --git a/rust/test/SP/BV-05-C.in b/rust/test/SP/BV-05-C.in index 7e0a9f7..4a5fe0e 100644 --- a/rust/test/SP/BV-05-C.in +++ b/rust/test/SP/BV-05-C.in @@ -81,7 +81,7 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } IUT -> Lower Tester: AuRand { transaction_id: 0, diff --git a/rust/test/SP/BV-06-C.in b/rust/test/SP/BV-06-C.in index 18318b3..6baa878 100644 --- a/rust/test/SP/BV-06-C.in +++ b/rust/test/SP/BV-06-C.in @@ -108,7 +108,7 @@ sequence! { procedure, context, transaction_id: 0, nonce: [0; 16], } - IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 } + IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 27 } IUT -> Lower Tester: Accepted { transaction_id: 0, accepted_opcode: Opcode::SimplePairingNumber, @@ -154,7 +154,7 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } IUT -> Upper Tester: AuthenticationComplete { status: ErrorCode::Success, diff --git a/rust/test/SP/BV-07-C.in b/rust/test/SP/BV-07-C.in index ef17f13..62570c6 100644 --- a/rust/test/SP/BV-07-C.in +++ b/rust/test/SP/BV-07-C.in @@ -94,7 +94,7 @@ sequence! { procedure, context, transaction_id: 0, accepted_opcode: Opcode::SimplePairingNumber, } - IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 } + IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 27 } Upper Tester -> IUT: UserConfirmationRequestReply { bd_addr: context.peer_address() } IUT -> Upper Tester: UserConfirmationRequestReplyComplete { num_hci_command_packets: 1, @@ -136,6 +136,6 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } } diff --git a/rust/test/SP/BV-08-C.in b/rust/test/SP/BV-08-C.in index 02bdb06..d9f22ee 100644 --- a/rust/test/SP/BV-08-C.in +++ b/rust/test/SP/BV-08-C.in @@ -108,7 +108,7 @@ sequence! { procedure, context, transaction_id: 0, nonce: [0; 16], } - IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 } + IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 27 } IUT -> Lower Tester: Accepted { transaction_id: 0, accepted_opcode: Opcode::SimplePairingNumber, diff --git a/rust/test/SP/BV-09-C.in b/rust/test/SP/BV-09-C.in index 4570c24..ff0f3dd 100644 --- a/rust/test/SP/BV-09-C.in +++ b/rust/test/SP/BV-09-C.in @@ -94,7 +94,7 @@ sequence! { procedure, context, transaction_id: 0, accepted_opcode: Opcode::SimplePairingNumber, } - IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 } + IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 27 } Upper Tester -> IUT: UserConfirmationRequestReply { bd_addr: context.peer_address() } IUT -> Upper Tester: UserConfirmationRequestReplyComplete { num_hci_command_packets: 1, diff --git a/rust/test/SP/BV-10-C.in b/rust/test/SP/BV-10-C.in index 7c9d431..7df69b6 100644 --- a/rust/test/SP/BV-10-C.in +++ b/rust/test/SP/BV-10-C.in @@ -108,7 +108,7 @@ sequence! { procedure, context, transaction_id: 0, nonce: [0; 16], } - IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 } + IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 27 } IUT -> Lower Tester: Accepted { transaction_id: 0, accepted_opcode: Opcode::SimplePairingNumber, diff --git a/rust/test/SP/BV-11-C.in b/rust/test/SP/BV-11-C.in index 619122d..1c46751 100644 --- a/rust/test/SP/BV-11-C.in +++ b/rust/test/SP/BV-11-C.in @@ -94,7 +94,7 @@ sequence! { procedure, context, transaction_id: 0, accepted_opcode: Opcode::SimplePairingNumber, } - IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 } + IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 27 } Upper Tester -> IUT: UserConfirmationRequestNegativeReply { bd_addr: context.peer_address() } IUT -> Upper Tester: UserConfirmationRequestNegativeReplyComplete { num_hci_command_packets: 1, diff --git a/rust/test/SP/BV-12-C.in b/rust/test/SP/BV-12-C.in index 218f3c0..9222ac6 100644 --- a/rust/test/SP/BV-12-C.in +++ b/rust/test/SP/BV-12-C.in @@ -165,7 +165,7 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } IUT -> Upper Tester: AuthenticationComplete { status: ErrorCode::Success, diff --git a/rust/test/SP/BV-13-C.in b/rust/test/SP/BV-13-C.in index 28618e3..62b9cdf 100644 --- a/rust/test/SP/BV-13-C.in +++ b/rust/test/SP/BV-13-C.in @@ -136,6 +136,6 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } } diff --git a/rust/test/SP/BV-18-C.in b/rust/test/SP/BV-18-C.in index 164679b..3319349 100644 --- a/rust/test/SP/BV-18-C.in +++ b/rust/test/SP/BV-18-C.in @@ -156,7 +156,7 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } IUT -> Upper Tester: AuthenticationComplete { status: ErrorCode::Success, diff --git a/rust/test/SP/BV-19-C.in b/rust/test/SP/BV-19-C.in index 1e9b3e1..9ed9c6d 100644 --- a/rust/test/SP/BV-19-C.in +++ b/rust/test/SP/BV-19-C.in @@ -138,6 +138,6 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } } diff --git a/rust/test/SP/BV-20-C.in b/rust/test/SP/BV-20-C.in index 4cb1186..aef563e 100644 --- a/rust/test/SP/BV-20-C.in +++ b/rust/test/SP/BV-20-C.in @@ -149,7 +149,7 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } IUT -> Upper Tester: AuthenticationComplete { status: ErrorCode::Success, diff --git a/rust/test/SP/BV-21-C.in b/rust/test/SP/BV-21-C.in index fc93935..00312c5 100644 --- a/rust/test/SP/BV-21-C.in +++ b/rust/test/SP/BV-21-C.in @@ -131,6 +131,6 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } } diff --git a/rust/test/SP/BV-22-C.in b/rust/test/SP/BV-22-C.in index 74e0c63..4090e89 100644 --- a/rust/test/SP/BV-22-C.in +++ b/rust/test/SP/BV-22-C.in @@ -162,7 +162,7 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } IUT -> Upper Tester: AuthenticationComplete { status: ErrorCode::Success, diff --git a/rust/test/SP/BV-23-C.in b/rust/test/SP/BV-23-C.in index c0f7dd6..11d4b53 100644 --- a/rust/test/SP/BV-23-C.in +++ b/rust/test/SP/BV-23-C.in @@ -144,6 +144,6 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } } diff --git a/rust/test/SP/BV-33-C.in b/rust/test/SP/BV-33-C.in index 90f5029..7784391 100644 --- a/rust/test/SP/BV-33-C.in +++ b/rust/test/SP/BV-33-C.in @@ -191,7 +191,7 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } IUT -> Upper Tester: AuthenticationComplete { status: ErrorCode::Success, diff --git a/rust/test/SP/BV-34-C.in b/rust/test/SP/BV-34-C.in index 39233bf..ead396e 100644 --- a/rust/test/SP/BV-34-C.in +++ b/rust/test/SP/BV-34-C.in @@ -152,6 +152,6 @@ sequence! { procedure, context, IUT -> Upper Tester: LinkKeyNotification { bd_addr: context.peer_address(), key_type: KeyType::AuthenticatedP192, - link_key: [0; 16], + link_key: [1; 16], } } diff --git a/test/LL/cig_reconfiguration.py b/test/LL/cig_reconfiguration.py new file mode 100644 index 0000000..ddde51d --- /dev/null +++ b/test/LL/cig_reconfiguration.py @@ -0,0 +1,282 @@ +# Copyright 2025 Google LLC +# +# 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 +# +# https://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. + +import hci_packets as hci +import link_layer_packets as ll +import llcp_packets as llcp +import random +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest, generate_rpa + + +class Test(ControllerTest): + + SDU_Interval_C_TO_P = 10000 # 10ms + SDU_Interval_P_TO_C = 10000 # 10ms + ISO_Interval = 16 # 20ms + Sub_Interval = 7500 # 7.5ms (approximation) + CIG_Sync_Delay = 7500 # 7.5ms (approximation) + CIS_Sync_Delay = 7500 # 7.5ms (approximation) + Worst_Case_SCA = hci.ClockAccuracy.PPM_500 + Packing = hci.Packing.SEQUENTIAL + Framing = hci.Enable.DISABLED + NSE = 4 + Max_SDU_C_TO_P = 130 + Max_SDU_P_TO_C = 130 + Max_PDU_C_TO_P = 130 + Max_PDU_P_TO_C = 130 + PHY_C_TO_P = 0x1 + PHY_P_TO_C = 0x1 + FT_C_TO_P = 1 + FT_P_TO_C = 1 + BN_C_TO_P = 2 + BN_P_TO_C = 2 + Max_Transport_Latency_C_TO_P = 40000 # 40ms + Max_Transport_Latency_P_TO_C = 40000 # 40ms + RTN_C_TO_P = 3 + RTN_P_TO_C = 3 + + # Verify that the CIG can be reconfigured while in configurable state. + async def test_reconfiguration_success(self): + # Test parameters. + cig_id = 0x12 + cis_id = 0x42 + peer_address = Address('aa:bb:cc:dd:ee:ff') + controller = self.controller + + # Enable Connected Isochronous Stream Host Support. + await self.enable_connected_isochronous_stream_host_support() + + # Create the CIG. + controller.send_cmd( + hci.LeSetCigParametersTest(cig_id=cig_id, + sdu_interval_c_to_p=self.SDU_Interval_C_TO_P, + sdu_interval_p_to_c=self.SDU_Interval_P_TO_C, + ft_c_to_p=self.FT_C_TO_P, + ft_p_to_c=self.FT_P_TO_C, + iso_interval=self.ISO_Interval, + worst_case_sca=self.Worst_Case_SCA, + packing=self.Packing, + framing=self.Framing, + cis_config=[ + hci.LeCisParametersTestConfig( + cis_id=cis_id, + nse=self.NSE, + max_sdu_c_to_p=self.Max_SDU_C_TO_P, + max_sdu_p_to_c=self.Max_SDU_P_TO_C, + max_pdu_c_to_p=self.Max_PDU_C_TO_P, + max_pdu_p_to_c=self.Max_PDU_P_TO_C, + phy_c_to_p=self.PHY_C_TO_P, + phy_p_to_c=self.PHY_P_TO_C, + bn_c_to_p=self.BN_C_TO_P, + bn_p_to_c=self.BN_P_TO_C) + ])) + + await self.expect_evt( + hci.LeSetCigParametersTestComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + cig_id=cig_id, + connection_handle=[self.Any])) + + # Attempt CIG reconfiguration with the same values. + controller.send_cmd( + hci.LeSetCigParametersTest(cig_id=cig_id, + sdu_interval_c_to_p=self.SDU_Interval_C_TO_P, + sdu_interval_p_to_c=self.SDU_Interval_P_TO_C, + ft_c_to_p=self.FT_C_TO_P, + ft_p_to_c=self.FT_P_TO_C, + iso_interval=self.ISO_Interval, + worst_case_sca=self.Worst_Case_SCA, + packing=self.Packing, + framing=self.Framing, + cis_config=[ + hci.LeCisParametersTestConfig( + cis_id=cis_id, + nse=self.NSE, + max_sdu_c_to_p=self.Max_SDU_C_TO_P, + max_sdu_p_to_c=self.Max_SDU_P_TO_C, + max_pdu_c_to_p=self.Max_PDU_C_TO_P, + max_pdu_p_to_c=self.Max_PDU_P_TO_C, + phy_c_to_p=self.PHY_C_TO_P, + phy_p_to_c=self.PHY_P_TO_C, + bn_c_to_p=self.BN_C_TO_P, + bn_p_to_c=self.BN_P_TO_C) + ])) + + await self.expect_evt( + hci.LeSetCigParametersTestComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + cig_id=cig_id, + connection_handle=[self.Any])) + + # Verify that the CIG reconfiguration attempt is rejected when the CIG is active. + async def test_reconfiguration_disallowed(self): + # Test parameters. + cig_id = 0x12 + cis_id = 0x42 + peer_address = Address('aa:bb:cc:dd:ee:ff') + controller = self.controller + + # Enable Connected Isochronous Stream Host Support. + await self.enable_connected_isochronous_stream_host_support() + + # Prelude: Establish an ACL connection as peripheral with the IUT. + acl_connection_handle = await self.establish_le_connection_central(peer_address) + + # Create the CIG. + controller.send_cmd( + hci.LeSetCigParametersTest(cig_id=cig_id, + sdu_interval_c_to_p=self.SDU_Interval_C_TO_P, + sdu_interval_p_to_c=self.SDU_Interval_P_TO_C, + ft_c_to_p=self.FT_C_TO_P, + ft_p_to_c=self.FT_P_TO_C, + iso_interval=self.ISO_Interval, + worst_case_sca=self.Worst_Case_SCA, + packing=self.Packing, + framing=self.Framing, + cis_config=[ + hci.LeCisParametersTestConfig( + cis_id=cis_id, + nse=self.NSE, + max_sdu_c_to_p=self.Max_SDU_C_TO_P, + max_sdu_p_to_c=self.Max_SDU_P_TO_C, + max_pdu_c_to_p=self.Max_PDU_C_TO_P, + max_pdu_p_to_c=self.Max_PDU_P_TO_C, + phy_c_to_p=self.PHY_C_TO_P, + phy_p_to_c=self.PHY_P_TO_C, + bn_c_to_p=self.BN_C_TO_P, + bn_p_to_c=self.BN_P_TO_C) + ])) + + event = await self.expect_evt( + hci.LeSetCigParametersTestComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + cig_id=cig_id, + connection_handle=[self.Any])) + cis_connection_handle = event.connection_handle[0] + + # The Upper Tester sends an HCI_LE_Create_CIS command to the IUT with the + # ACL_Connection_Handle of the established ACL connection and CIS_Count set to 1. The Upper + # Tester receives a Status of Success from the IUT. + controller.send_cmd( + hci.LeCreateCis(cis_config=[ + hci.LeCreateCisConfig(cis_connection_handle=cis_connection_handle, + acl_connection_handle=acl_connection_handle) + ])) + + await self.expect_evt( + hci.LeCreateCisStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + # The Lower Tester receives an LL_CIS_REQ PDU from the IUT with all fields set to valid values. + # CIS_Offset_Min is a value between 500µs and TSPX_conn_interval, CIS_Offset_Max is a value + # between CIS_Offset_Min and the CIS_Offset_Max value as calculated in [14] Section 2.4.2.29 + # using TSPX_conn_interval as the value of connInterval, and connEventCount is the reference + # event anchor point for which the offsets applied. + cis_req = await self.expect_llcp(source_address=controller.address, + destination_address=peer_address, + expected_pdu=llcp.CisReq( + cig_id=cig_id, + cis_id=cis_id, + phy_c_to_p=hci.PhyType.LE_1M, + phy_p_to_c=hci.PhyType.LE_1M, + framed=self.Framing == hci.Enable.ENABLED, + max_sdu_c_to_p=self.Max_SDU_C_TO_P, + max_sdu_p_to_c=self.Max_SDU_P_TO_C, + sdu_interval_c_to_p=self.SDU_Interval_C_TO_P, + sdu_interval_p_to_c=self.SDU_Interval_P_TO_C, + max_pdu_c_to_p=self.Max_PDU_C_TO_P, + max_pdu_p_to_c=self.Max_PDU_P_TO_C, + nse=self.NSE, + sub_interval=self.Any, + bn_p_to_c=self.BN_C_TO_P, + bn_c_to_p=self.BN_P_TO_C, + ft_c_to_p=self.FT_C_TO_P, + ft_p_to_c=self.FT_P_TO_C, + iso_interval=self.ISO_Interval, + cis_offset_min=self.Any, + cis_offset_max=self.Any, + conn_event_count=0)) + # The Lower Tester sends an LL_CIS_RSP PDU to the IUT. + controller.send_llcp(source_address=peer_address, + destination_address=controller.address, + pdu=llcp.CisRsp(cis_offset_min=cis_req.cis_offset_min, + cis_offset_max=cis_req.cis_offset_max, + conn_event_count=0)) + + # The Lower Tester receives an LL_CIS_IND from the IUT where the CIS_Offset is the time (ms) + # from the start of the ACL connection event in connEvent Count to the first CIS anchor point, the + # CIS_Sync_Delay is CIG_Sync_Delay minus the offset from the CIG reference point to the CIS + # anchor point in s, and the connEventCount is the CIS_Offset reference point. + cis_ind = await self.expect_llcp(source_address=controller.address, + destination_address=peer_address, + expected_pdu=llcp.CisInd(aa=0, + cis_offset=self.Any, + cig_sync_delay=self.Any, + cis_sync_delay=self.Any, + conn_event_count=0)) + + # 7. The Upper Tester receives a successful HCI_LE_CIS_Established event with the NSE, BN, FT, + # and Max_PDU parameters as set in step 1 from the IUT, after the first CIS packet sent by the LT. + # The Connection_Handle parameter is set to the value provided in the HCI_LE_Create_CIS + # command. + await self.expect_evt( + hci.LeCisEstablishedV1(status=ErrorCode.SUCCESS, + connection_handle=cis_connection_handle, + cig_sync_delay=cis_ind.cig_sync_delay, + cis_sync_delay=cis_ind.cis_sync_delay, + transport_latency_c_to_p=self.Any, + transport_latency_p_to_c=self.Any, + phy_c_to_p=hci.SecondaryPhyType.LE_1M, + phy_p_to_c=hci.SecondaryPhyType.LE_1M, + nse=self.NSE, + bn_c_to_p=self.BN_C_TO_P, + bn_p_to_c=self.BN_P_TO_C, + ft_c_to_p=self.FT_C_TO_P, + ft_p_to_c=self.FT_P_TO_C, + max_pdu_c_to_p=self.Max_PDU_C_TO_P, + max_pdu_p_to_c=self.Max_PDU_P_TO_C, + iso_interval=self.ISO_Interval)) + + # Attempt CIG reconfiguration with the same values. + controller.send_cmd( + hci.LeSetCigParametersTest(cig_id=cig_id, + sdu_interval_c_to_p=self.SDU_Interval_C_TO_P, + sdu_interval_p_to_c=self.SDU_Interval_P_TO_C, + ft_c_to_p=self.FT_C_TO_P, + ft_p_to_c=self.FT_P_TO_C, + iso_interval=self.ISO_Interval, + worst_case_sca=self.Worst_Case_SCA, + packing=self.Packing, + framing=self.Framing, + cis_config=[ + hci.LeCisParametersTestConfig( + cis_id=cis_id, + nse=self.NSE, + max_sdu_c_to_p=self.Max_SDU_C_TO_P, + max_sdu_p_to_c=self.Max_SDU_P_TO_C, + max_pdu_c_to_p=self.Max_PDU_C_TO_P, + max_pdu_p_to_c=self.Max_PDU_P_TO_C, + phy_c_to_p=self.PHY_C_TO_P, + phy_p_to_c=self.PHY_P_TO_C, + bn_c_to_p=self.BN_C_TO_P, + bn_p_to_c=self.BN_P_TO_C) + ])) + + await self.expect_evt( + hci.LeSetCigParametersTestComplete(status=ErrorCode.COMMAND_DISALLOWED, + num_hci_command_packets=1, + cig_id=cig_id, + connection_handle=[]))