Skip to content

Commit

Permalink
pw_bluetooth_proxy: Automatically track BR/EDR ACL connections
Browse files Browse the repository at this point in the history
Handle ACL connection/disconnection events by creating a connection
object to track in the proxy. This will allow signaling channel events
to be processed.

Bug: 379558046
Change-Id: I0e3bfcd3205a1476d51ec5d3e5c2b2cc294eb4c3
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/249717
Docs-Not-Needed: Ben Lawson <[email protected]>
Pigweed-Auto-Submit: Austin Foxley <[email protected]>
Lint: Lint 🤖 <[email protected]>
Commit-Queue: Auto-Submit <[email protected]>
Reviewed-by: Ben Lawson <[email protected]>
Presubmit-Verified: CQ Bot Account <[email protected]>
  • Loading branch information
afoxley authored and CQ Bot Account committed Nov 27, 2024
1 parent acad654 commit 6ab78b0
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 1 deletion.
31 changes: 31 additions & 0 deletions pw_bluetooth_proxy/acl_data_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,36 @@ void AclDataChannel::HandleNumberOfCompletedPacketsEvent(
}
}

// Create new tracked connection and pass on to host.
void AclDataChannel::HandleConnectionCompleteEvent(
H4PacketWithHci&& h4_packet) {
pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
Result<emboss::ConnectionCompleteEventView> connection_complete_event =
MakeEmbossView<emboss::ConnectionCompleteEventView>(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<emboss::DisconnectionCompleteEventView> dc_event =
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions pw_bluetooth_proxy/proxy_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
64 changes: 63 additions & 1 deletion pw_bluetooth_proxy/proxy_host_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t, emboss::ConnectionCompleteEvent::IntrinsicSizeInBytes()>
hci_arr_dc{};
H4PacketWithHci dc_event{emboss::H4PacketType::EVENT, hci_arr_dc};
PW_TRY_ASSIGN(auto view,
MakeEmbossWriter<emboss::ConnectionCompleteEventWriter>(
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,
Expand Down Expand Up @@ -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<void(H4PacketWithH4 && packet)> send_to_controller_fn(
[]([[maybe_unused]] H4PacketWithH4&& packet) {});

pw::Function<void(H4PacketWithHci && packet)> 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<void(H4PacketWithH4 && packet)> send_to_controller_fn(
[]([[maybe_unused]] H4PacketWithH4&& packet) {});

pw::Function<void(H4PacketWithHci && packet)> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 6ab78b0

Please sign in to comment.