Skip to content

Commit

Permalink
pw_bluetooth_proxy: Add automatic LE connection tracking
Browse files Browse the repository at this point in the history
Change-Id: I71de373c4acc59e60fbfeb04530800b3bbb54636
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/251053
Lint: Lint 🤖 <[email protected]>
Docs-Not-Needed: Ben Lawson <[email protected]>
Reviewed-by: Ben Lawson <[email protected]>
Pigweed-Auto-Submit: Austin Foxley <[email protected]>
Commit-Queue: Auto-Submit <[email protected]>
  • Loading branch information
afoxley authored and CQ Bot Account committed Nov 27, 2024
1 parent 6ab78b0 commit 91d9808
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 7 deletions.
30 changes: 30 additions & 0 deletions pw_bluetooth/public/pw_bluetooth/hci_common.emb
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,36 @@ enum EventCode:
VENDOR_DEBUG = 0xFF


enum LeSubEventCode:
-- HCI LE Meta event SubEvent codes
-- Core Spec v6.0 Vol 4, Part E, Section 7.7.65
[maximum_bits: 8]
CONNECTION_COMPLETE = 0x01
ADVERTISING_REPORT = 0x02
CONNECTION_UPDATE_COMPLETE = 0x03
READ_REMOTE_FEATURES_COMPLETE = 0x04
LONG_TERM_KEY_REQUEST = 0x05
REMOTE_CONNECTION_PARAMETER_REQUEST = 0x06
DATA_LENGTH_CHANGE = 0x07
READ_LOCAL_P256_PUBLIC_KEY_COMPLETE = 0x08
GENERATE_DH_KEY_COMPLETE = 0x09
ENHANCED_CONNECTION_COMPLETE_V1 = 0x0A
DIRECTED_ADVERTISING_REPORT = 0x0B
PHY_UPDATE_COMPLETE = 0x0C
EXTENDED_ADVERTISING_REPORT = 0x0D
PERIODIC_ADVERTISING_SYNC_ESTABLISHED = 0x0E
PERIODIC_ADVERTISING_REPORT = 0x0F
PERIODIC_ADVERTISING_SYNC_LOST = 0x10
SCAN_TIMEOUT = 0x11
ADVERTISING_SET_TERMINATED = 0x12
SCAN_REQUEST_RECEIVED = 0x13
CHANNEL_SELECTION_ALGORITHM = 0x14
CIS_ESTABLISHED = 0x19
REQUEST_PEER_SCA_COMPLETE = 0x1F
CIS_REQUEST = 0x1A
ENHANCED_CONNECTION_COMPLETE_V2 = 0x29


enum MajorDeviceClass:
[maximum_bits: 5]
MISCELLANEOUS = 0x00
Expand Down
44 changes: 38 additions & 6 deletions pw_bluetooth/public/pw_bluetooth/hci_events.emb
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ struct ExtendedInquiryResultEvent:
[requires: -127 <= this <= 20]

$next [+240] UInt:8[240] extended_inquiry_response
-- Extended inquiey response data as defined in Vol 3, Part C, Sec 8
-- Extended inquiry response data as defined in Vol 3, Part C, Sec 8


struct EncryptionKeyRefreshCompleteEvent:
Expand Down Expand Up @@ -1669,8 +1669,9 @@ struct UserPasskeyNotificationEvent:
struct LEMetaEvent:
-- 7.7.65 LE Meta event
let hdr_size = hci.EventHeader.$size_in_bytes
0 [+hdr_size] hci.EventHeader header
$next [+1] UInt subevent_code
0 [+hdr_size] hci.EventHeader header
hdr_size [+1] UInt subevent_code
hdr_size [+1] hci.LeSubEventCode subevent_code_enum
-- The event code for the LE subevent.


Expand Down Expand Up @@ -1840,10 +1841,41 @@ struct LEEnhancedConnectionCompleteSubeventV1:
$next [+1] LEClockAccuracy central_clock_accuracy
-- Only valid for a peripheral. On a central, this parameter shall be set to 0x00.

# 7.7.65.10 LE Enhanced Connection Complete event
# HCI_LE_Enhanced_Connection_Complete
# TODO: b/265052417 - Definition needs to be added

struct LEEnhancedConnectionCompleteSubeventV2:
-- 7.7.65.10 LE Enhanced Connection Complete event
-- HCI_LE_Enhanced_Connection_Complete
0 [+LEMetaEvent.$size_in_bytes] LEMetaEvent le_meta_event
$next [+1] hci.StatusCode status
$next [+2] UInt connection_handle
[requires: 0x0000 <= this <= 0x0EFF]

$next [+1] hci.ConnectionRole role
$next [+1] hci.LEAddressType peer_address_type
$next [+hci.BdAddr.$size_in_bytes] hci.BdAddr peer_address
$next [+hci.BdAddr.$size_in_bytes] hci.BdAddr local_resolvable_private_address
$next [+hci.BdAddr.$size_in_bytes] hci.BdAddr peer_resolvable_private_address
$next [+2] UInt connection_interval
-- Time: N * 1.25 ms
-- Range: 7.5 ms to 4 s
[requires: 0x0006 <= this <= 0x0C80]

$next [+2] UInt peripheral_latency
[requires: 0x0000 <= this <= 0x01F3]

$next [+2] UInt supervision_timeout
-- Time: N * 10 ms
-- Range: 100 ms to 32 s
[requires: 0x000A <= this <= 0x0C80]

$next [+1] LEClockAccuracy central_clock_accuracy
-- Only valid for a peripheral. On a central, this parameter shall be set to 0x00.

$next [+1] UInt advertising_handle
-- Used to identify an advertising set

$next [+2] UInt sync_handle
-- Used to identify the periodic advertising train.

# 7.7.65.11 LE Directed Advertising Report event
# HCI_LE_Directed_Advertising_Report
Expand Down
65 changes: 64 additions & 1 deletion pw_bluetooth_proxy/acl_data_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ 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();
Expand Down Expand Up @@ -241,6 +240,70 @@ void AclDataChannel::HandleConnectionCompleteEvent(
hci_transport_.SendToHost(std::move(h4_packet));
}

void AclDataChannel::HandleLeConnectionCompleteEvent(
uint16_t connection_handle, emboss::StatusCode status) {
if (status != emboss::StatusCode::SUCCESS) {
return;
}

if (CreateAclConnection(connection_handle, AclTransportType::kLe) ==
Status::ResourceExhausted()) {
PW_LOG_ERROR(
"Could not track connection like requested. Max connections "
"reached.");
}
}

void AclDataChannel::HandleLeConnectionCompleteEvent(
H4PacketWithHci&& h4_packet) {
pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
Result<emboss::LEConnectionCompleteSubeventView> event =
MakeEmbossView<emboss::LEConnectionCompleteSubeventView>(hci_buffer);
if (!event.ok()) {
hci_transport_.SendToHost(std::move(h4_packet));
return;
}

HandleLeConnectionCompleteEvent(event->connection_handle().Read(),
event->status().Read());

hci_transport_.SendToHost(std::move(h4_packet));
}

void AclDataChannel::HandleLeEnhancedConnectionCompleteV1Event(
H4PacketWithHci&& h4_packet) {
pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
Result<emboss::LEEnhancedConnectionCompleteSubeventV1View> event =
MakeEmbossView<emboss::LEEnhancedConnectionCompleteSubeventV1View>(
hci_buffer);
if (!event.ok()) {
hci_transport_.SendToHost(std::move(h4_packet));
return;
}

HandleLeConnectionCompleteEvent(event->connection_handle().Read(),
event->status().Read());

hci_transport_.SendToHost(std::move(h4_packet));
}

void AclDataChannel::HandleLeEnhancedConnectionCompleteV2Event(
H4PacketWithHci&& h4_packet) {
pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
Result<emboss::LEEnhancedConnectionCompleteSubeventV2View> event =
MakeEmbossView<emboss::LEEnhancedConnectionCompleteSubeventV2View>(
hci_buffer);
if (!event.ok()) {
hci_transport_.SendToHost(std::move(h4_packet));
return;
}

HandleLeConnectionCompleteEvent(event->connection_handle().Read(),
event->status().Read());

hci_transport_.SendToHost(std::move(h4_packet));
}

void AclDataChannel::HandleDisconnectionCompleteEvent(
H4PacketWithHci&& h4_packet) {
Result<emboss::DisconnectionCompleteEventView> dc_event =
Expand Down
39 changes: 39 additions & 0 deletions pw_bluetooth_proxy/proxy_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ void ProxyHost::HandleEventFromController(H4PacketWithHci&& h4_packet) {
acl_data_channel_.HandleConnectionCompleteEvent(std::move(h4_packet));
break;
}
case emboss::EventCode::LE_META_EVENT: {
HandleLeMetaEvent(std::move(h4_packet));
break;
}
default: {
hci_transport_.SendToHost(std::move(h4_packet));
return;
Expand Down Expand Up @@ -210,6 +214,41 @@ void ProxyHost::HandleAclFromController(H4PacketWithHci&& h4_packet) {
}
}

void ProxyHost::HandleLeMetaEvent(H4PacketWithHci&& h4_packet) {
pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
Result<emboss::LEMetaEventView> le_meta_event_view =
MakeEmbossView<emboss::LEMetaEventView>(hci_buffer);
if (!le_meta_event_view.ok()) {
PW_LOG_ERROR(
"Buffer is too small for LE_META_EVENT event. So will not process.");
hci_transport_.SendToHost(std::move(h4_packet));
return;
}

PW_MODIFY_DIAGNOSTICS_PUSH();
PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
switch (le_meta_event_view->subevent_code_enum().Read()) {
case emboss::LeSubEventCode::CONNECTION_COMPLETE: {
acl_data_channel_.HandleLeConnectionCompleteEvent(std::move(h4_packet));
return;
}
case emboss::LeSubEventCode::ENHANCED_CONNECTION_COMPLETE_V1: {
acl_data_channel_.HandleLeEnhancedConnectionCompleteV1Event(
std::move(h4_packet));
return;
}
case emboss::LeSubEventCode::ENHANCED_CONNECTION_COMPLETE_V2: {
acl_data_channel_.HandleLeEnhancedConnectionCompleteV2Event(
std::move(h4_packet));
return;
}
default:
break;
}
PW_MODIFY_DIAGNOSTICS_POP();
hci_transport_.SendToHost(std::move(h4_packet));
}

void ProxyHost::HandleCommandCompleteEvent(H4PacketWithHci&& h4_packet) {
pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
Result<emboss::CommandCompleteEventView> command_complete_event =
Expand Down
41 changes: 41 additions & 0 deletions pw_bluetooth_proxy/proxy_host_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,28 @@ Status SendConnectionCompleteEvent(ProxyHost& proxy,
return OkStatus();
}

// Send a LE_Connection_Complete event to `proxy` indicating the provided
// `handle` has disconnected.
Status SendLeConnectionCompleteEvent(ProxyHost& proxy,
uint16_t handle,
emboss::StatusCode status) {
std::array<uint8_t,
emboss::LEConnectionCompleteSubevent::IntrinsicSizeInBytes()>
hci_arr_dc{};
H4PacketWithHci dc_event{emboss::H4PacketType::EVENT, hci_arr_dc};
PW_TRY_ASSIGN(auto view,
MakeEmbossWriter<emboss::LEConnectionCompleteSubeventWriter>(
dc_event.GetHciSpan()));
view.le_meta_event().header().event_code_enum().Write(
emboss::EventCode::LE_META_EVENT);
view.le_meta_event().subevent_code_enum().Write(
emboss::LeSubEventCode::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 @@ -4700,6 +4722,25 @@ TEST(ProxyHostConnectionEventTest,
EXPECT_EQ(host_called, 1U);
}

TEST(ProxyHostConnectionEventTest, LeConnectionCompletePassthroughOk) {
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(
SendLeConnectionCompleteEvent(proxy, 1, emboss::StatusCode::SUCCESS));
EXPECT_EQ(host_called, 1U);
}

// TODO: https://pwbug.dev/360929142 - Add many more tests exercising queueing
// credit-based control flow.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ class AclDataChannel {
// Create new tracked connection and pass on to host.
void HandleConnectionCompleteEvent(H4PacketWithHci&& h4_packet);

// Create new tracked connection and pass on to host.
void HandleLeConnectionCompleteEvent(H4PacketWithHci&& h4_packet);

// Create new tracked connection and pass on to host.
void HandleLeEnhancedConnectionCompleteV1Event(H4PacketWithHci&& h4_packet);

// Create new tracked connection and pass on to host.
void HandleLeEnhancedConnectionCompleteV2Event(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 Expand Up @@ -258,6 +267,9 @@ class AclDataChannel {
const Credits& LookupCredits(AclTransportType transport) const
PW_EXCLUSIVE_LOCKS_REQUIRED(credit_allocation_mutex_);

void HandleLeConnectionCompleteEvent(uint16_t connection_handle,
emboss::StatusCode status);

// Maximum number of simultaneous credit-allocated LE connections supported.
// TODO: https://pwbug.dev/349700888 - Make size configurable.
static constexpr size_t kMaxConnections = 10;
Expand Down
3 changes: 3 additions & 0 deletions pw_bluetooth_proxy/public/pw_bluetooth_proxy/proxy_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ class ProxyHost {
// Handle HCI ACL data packet from the controller.
void HandleAclFromController(H4PacketWithHci&& h4_packet);

// Process an LE_META_EVENT
void HandleLeMetaEvent(H4PacketWithHci&& h4_packet);

// Process a Command_Complete event.
void HandleCommandCompleteEvent(H4PacketWithHci&& h4_packet);

Expand Down

0 comments on commit 91d9808

Please sign in to comment.