diff --git a/pw_bluetooth_proxy/acl_data_channel.cc b/pw_bluetooth_proxy/acl_data_channel.cc index de1bd6aac..b70cb41ba 100644 --- a/pw_bluetooth_proxy/acl_data_channel.cc +++ b/pw_bluetooth_proxy/acl_data_channel.cc @@ -211,6 +211,36 @@ void AclDataChannel::HandleNumberOfCompletedPacketsEvent( } } +// Create new tracked connection and pass on to host. +void AclDataChannel::HandleConnectionCompleteEvent( + H4PacketWithHci&& h4_packet) { + pw::span hci_buffer = h4_packet.GetHciSpan(); + Result connection_complete_event = + MakeEmbossView(hci_buffer); + if (!connection_complete_event.ok()) { + hci_transport_.SendToHost(std::move(h4_packet)); + return; + } + + if (connection_complete_event->status().Read() != + emboss::StatusCode::SUCCESS) { + hci_transport_.SendToHost(std::move(h4_packet)); + return; + } + + const uint16_t conn_handle = + connection_complete_event->connection_handle().Read(); + + if (CreateAclConnection(conn_handle, AclTransportType::kBrEdr) == + Status::ResourceExhausted()) { + PW_LOG_ERROR( + "Could not track connection like requested. Max connections " + "reached."); + } + + hci_transport_.SendToHost(std::move(h4_packet)); +} + void AclDataChannel::HandleDisconnectionCompleteEvent( H4PacketWithHci&& h4_packet) { Result dc_event = @@ -246,6 +276,7 @@ void AclDataChannel::HandleDisconnectionCompleteEvent( LookupCredits(connection_ptr->transport()) .MarkCompleted(connection_ptr->num_pending_packets()); } + active_acl_connections_.erase(connection_ptr); } else { if (connection_ptr->num_pending_packets() > 0) { diff --git a/pw_bluetooth_proxy/proxy_host.cc b/pw_bluetooth_proxy/proxy_host.cc index 1e006a016..8c75b015e 100644 --- a/pw_bluetooth_proxy/proxy_host.cc +++ b/pw_bluetooth_proxy/proxy_host.cc @@ -145,6 +145,10 @@ void ProxyHost::HandleEventFromController(H4PacketWithHci&& h4_packet) { HandleCommandCompleteEvent(std::move(h4_packet)); break; } + case emboss::EventCode::CONNECTION_COMPLETE: { + acl_data_channel_.HandleConnectionCompleteEvent(std::move(h4_packet)); + break; + } default: { hci_transport_.SendToHost(std::move(h4_packet)); return; diff --git a/pw_bluetooth_proxy/proxy_host_test.cc b/pw_bluetooth_proxy/proxy_host_test.cc index 7f8557211..b683e237c 100644 --- a/pw_bluetooth_proxy/proxy_host_test.cc +++ b/pw_bluetooth_proxy/proxy_host_test.cc @@ -153,6 +153,24 @@ Status SendNumberOfCompletedPackets( return OkStatus(); } +// Send a Connection_Complete event to `proxy` indicating the provided +// `handle` has disconnected. +Status SendConnectionCompleteEvent(ProxyHost& proxy, + uint16_t handle, + emboss::StatusCode status) { + std::array + hci_arr_dc{}; + H4PacketWithHci dc_event{emboss::H4PacketType::EVENT, hci_arr_dc}; + PW_TRY_ASSIGN(auto view, + MakeEmbossWriter( + dc_event.GetHciSpan())); + view.header().event_code_enum().Write(emboss::EventCode::CONNECTION_COMPLETE); + view.status().Write(status); + view.connection_handle().Write(handle); + proxy.HandleH4HciFromController(std::move(dc_event)); + return OkStatus(); +} + // Send a Disconnection_Complete event to `proxy` indicating the provided // `handle` has disconnected. Status SendDisconnectionCompleteEvent(ProxyHost& proxy, @@ -4638,7 +4656,51 @@ TEST(RfcommReadTest, InvalidReads) { EXPECT_EQ(capture.host_called, 2); } -// TODO: https://pwbug.dev/360929142 - Add many more tests exercising queueing + +// ########## ProxyHostConnectionEventTest + +TEST(ProxyHostConnectionEventTest, ConnectionCompletePassthroughOk) { + size_t host_called = 0; + pw::Function send_to_controller_fn( + []([[maybe_unused]] H4PacketWithH4&& packet) {}); + + pw::Function send_to_host_fn( + [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) { + ++host_called; + }); + + ProxyHost proxy = ProxyHost(std::move(send_to_host_fn), + std::move(send_to_controller_fn), + /*le_acl_credits_to_reserve=*/0); + + PW_TEST_EXPECT_OK( + SendConnectionCompleteEvent(proxy, 1, emboss::StatusCode::SUCCESS)); + EXPECT_EQ(host_called, 1U); + + PW_TEST_EXPECT_OK(SendDisconnectionCompleteEvent(proxy, 1)); + EXPECT_EQ(host_called, 2U); +} + +TEST(ProxyHostConnectionEventTest, + ConnectionCompleteWithErrorStatusPassthroughOk) { + size_t host_called = 0; + pw::Function send_to_controller_fn( + []([[maybe_unused]] H4PacketWithH4&& packet) {}); + + pw::Function send_to_host_fn( + [&host_called]([[maybe_unused]] H4PacketWithHci&& packet) { + ++host_called; + }); + + ProxyHost proxy = ProxyHost(std::move(send_to_host_fn), + std::move(send_to_controller_fn), + /*le_acl_credits_to_reserve=*/0); + + PW_TEST_EXPECT_OK(SendConnectionCompleteEvent( + proxy, 1, emboss::StatusCode::CONNECTION_FAILED_TO_BE_ESTABLISHED)); + EXPECT_EQ(host_called, 1U); +} + +// TODO: https://pwbug.dev/360929142 - Add many more tests exercising queueing // credit-based control flow. } // namespace diff --git a/pw_bluetooth_proxy/public/pw_bluetooth_proxy/internal/acl_data_channel.h b/pw_bluetooth_proxy/public/pw_bluetooth_proxy/internal/acl_data_channel.h index 0701d308a..ac097d663 100644 --- a/pw_bluetooth_proxy/public/pw_bluetooth_proxy/internal/acl_data_channel.h +++ b/pw_bluetooth_proxy/public/pw_bluetooth_proxy/internal/acl_data_channel.h @@ -80,6 +80,9 @@ class AclDataChannel { // Reclaim any credits we have associated with the removed connection. void HandleDisconnectionCompleteEvent(H4PacketWithHci&& h4_packet); + // Create new tracked connection and pass on to host. + void HandleConnectionCompleteEvent(H4PacketWithHci&& h4_packet); + /// Indicates whether the proxy has the capability of sending ACL packets. /// Note that this indicates intention, so it can be true even if the proxy /// has not yet or has been unable to reserve credits from the host.