Skip to content

Commit

Permalink
pw_bluetooth_proxy: Prepare for BR/EDR ACL credit support
Browse files Browse the repository at this point in the history
Refactor credit management a bit inside AclDataChannel to support this.

Doesn't yet handle deallocation for BR/EDR as that will require tracking
transport type for connections.

Does not support the case of shared LE/classic credits, as we don't have
a need to support a controller that requires that at the moment.

Bug: 379172336
Change-Id: I44d5a41dd57130b59a75ce10ca18b8210756874c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/246732
Reviewed-by: Richard Pasek <[email protected]>
Commit-Queue: Austin Foxley <[email protected]>
Lint: Lint 🤖 <[email protected]>
Reviewed-by: Ben Lawson <[email protected]>
Reviewed-by: David Rees <[email protected]>
Docs-Not-Needed: Austin Foxley <[email protected]>
  • Loading branch information
afoxley authored and CQ Bot Account committed Nov 17, 2024
1 parent 0959346 commit aeecb42
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 80 deletions.
125 changes: 90 additions & 35 deletions pw_bluetooth_proxy/acl_data_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,46 +28,80 @@ namespace pw::bluetooth::proxy {

void AclDataChannel::Reset() {
std::lock_guard lock(credit_allocation_mutex_);
initialized_ = false;
proxy_max_le_acl_packets_ = 0;
proxy_pending_le_acl_packets_ = 0;
le_credits_.Reset();
br_edr_credits_.Reset();
active_le_acl_connections_.clear();
}

template <class EventT>
void AclDataChannel::ProcessSpecificLEReadBufferSizeCommandCompleteEvent(
EventT read_buffer_event) {
credit_allocation_mutex_.lock();
if (initialized_) {
PW_LOG_WARN(
"AclDataChannel is already initialized, but encountered another "
"ReadBufferSizeCommandCompleteEvent.");
}
void AclDataChannel::Credits::Reset() {
proxy_max_ = 0;
proxy_pending_ = 0;
}

initialized_ = true;
uint16_t AclDataChannel::Credits::Reserve(uint16_t controller_max) {
PW_CHECK(proxy_max_ == 0,
"AclDataChannel is already initialized, but encountered another "
"ReadBufferSizeCommandCompleteEvent.");

proxy_max_ = std::min(controller_max, to_reserve_);
const uint16_t host_max = controller_max - proxy_max_;

uint16_t controller_max_le_acl_packets =
read_buffer_event.total_num_le_acl_data_packets().Read();
proxy_max_le_acl_packets_ =
std::min(controller_max_le_acl_packets, le_acl_credits_to_reserve_);
uint16_t host_max_le_acl_packets =
controller_max_le_acl_packets - proxy_max_le_acl_packets_;
read_buffer_event.total_num_le_acl_data_packets().Write(
host_max_le_acl_packets);
PW_LOG_INFO(
"Bluetooth Proxy reserved %d ACL data credits. Passed %d on to host.",
proxy_max_le_acl_packets_,
host_max_le_acl_packets);
proxy_max_,
host_max);

if (proxy_max_le_acl_packets_ < le_acl_credits_to_reserve_) {
if (proxy_max_ < to_reserve_) {
PW_LOG_ERROR(
"Only was able to reserve %d acl data credits rather than the "
"configured %d from the controller provided's data credits of %d. ",
proxy_max_le_acl_packets_,
le_acl_credits_to_reserve_,
controller_max_le_acl_packets);
proxy_max_,
to_reserve_,
controller_max);
}

return host_max;
}

Status AclDataChannel::Credits::MarkPending(uint16_t num_credits) {
if (num_credits > Available()) {
return Status::ResourceExhausted();
}

proxy_pending_ += num_credits;

return OkStatus();
}

void AclDataChannel::Credits::MarkCompleted(uint16_t num_credits) {
if (num_credits > proxy_pending_) {
PW_LOG_ERROR("Tried to mark completed more packets than were pending.");
proxy_pending_ = 0;
} else {
proxy_pending_ -= num_credits;
}
}

void AclDataChannel::ProcessReadBufferSizeCommandCompleteEvent(
emboss::ReadBufferSizeCommandCompleteEventWriter read_buffer_event) {
credit_allocation_mutex_.lock();
const uint16_t controller_max =
read_buffer_event.total_num_acl_data_packets().Read();
const uint16_t host_max = br_edr_credits_.Reserve(controller_max);
read_buffer_event.total_num_acl_data_packets().Write(host_max);
credit_allocation_mutex_.unlock();

l2cap_channel_manager_.DrainWriteChannelQueues();
}

template <class EventT>
void AclDataChannel::ProcessSpecificLEReadBufferSizeCommandCompleteEvent(
EventT read_buffer_event) {
credit_allocation_mutex_.lock();
const uint16_t controller_max =
read_buffer_event.total_num_le_acl_data_packets().Read();
const uint16_t host_max = le_credits_.Reserve(controller_max);
read_buffer_event.total_num_le_acl_data_packets().Write(host_max);
credit_allocation_mutex_.unlock();

// Send packets that may have queued before we acquired any LE ACL credits.
Expand Down Expand Up @@ -120,10 +154,13 @@ void AclDataChannel::HandleNumberOfCompletedPacketsEvent(
uint16_t num_pending_packets = connection_ptr->num_pending_packets();
uint16_t num_reclaimed =
std::min(num_completed_packets, num_pending_packets);

if (num_reclaimed > 0) {
did_reclaim_credits = true;
}
proxy_pending_le_acl_packets_ -= num_reclaimed;
// TODO: https://pwbug.dev/379172336 - Select correct Credits to decrement.
le_credits_.MarkCompleted(num_reclaimed);

connection_ptr->set_num_pending_packets(num_pending_packets -
num_reclaimed);

Expand Down Expand Up @@ -174,7 +211,9 @@ void AclDataChannel::HandleDisconnectionCompleteEvent(
"with packets in flight. Releasing associated credits",
cpp23::to_underlying(dc_event->reason().Read()),
conn_handle);
proxy_pending_le_acl_packets_ -= connection_ptr->num_pending_packets();
// TODO: https://pwbug.dev/379172336 - Select correct Credits to
// decrement.
le_credits_.MarkCompleted(connection_ptr->num_pending_packets());
}
active_le_acl_connections_.erase(connection_ptr);
} else {
Expand All @@ -190,26 +229,42 @@ void AclDataChannel::HandleDisconnectionCompleteEvent(
hci_transport_.SendToHost(std::move(h4_packet));
}

uint16_t AclDataChannel::GetLeAclCreditsToReserve() const {
return le_acl_credits_to_reserve_;
bool AclDataChannel::HasSendLeAclCapability() const {
credit_allocation_mutex_.lock();
bool has_send_capability = le_credits_.HasSendCapability();
credit_allocation_mutex_.unlock();
return has_send_capability;
}

bool AclDataChannel::HasSendBrEdrAclCapability() const {
credit_allocation_mutex_.lock();
bool has_send_capability = br_edr_credits_.HasSendCapability();
credit_allocation_mutex_.unlock();
return has_send_capability;
}

uint16_t AclDataChannel::GetNumFreeLeAclPackets() const {
credit_allocation_mutex_.lock();
uint16_t free_packets =
proxy_max_le_acl_packets_ - proxy_pending_le_acl_packets_;
uint16_t free_packets = le_credits_.Remaining();
credit_allocation_mutex_.unlock();
return free_packets;
}

uint16_t AclDataChannel::GetNumFreeBrEdrAclPackets() const {
credit_allocation_mutex_.lock();
uint16_t free_packets = br_edr_credits_.Remaining();
credit_allocation_mutex_.unlock();
return free_packets;
}

pw::Status AclDataChannel::SendAcl(H4PacketWithH4&& h4_packet) {
credit_allocation_mutex_.lock();
if (proxy_pending_le_acl_packets_ == proxy_max_le_acl_packets_) {
// TODO: https://pwbug.dev/379172336 - Select correct Credits to increment.
if (const auto status = le_credits_.MarkPending(1); !status.ok()) {
PW_LOG_WARN("No ACL send credits available. So will not send.");
credit_allocation_mutex_.unlock();
return pw::Status::Unavailable();
}
++proxy_pending_le_acl_packets_;

Result<emboss::AclDataFrameHeaderView> acl_view =
MakeEmbossView<emboss::AclDataFrameHeaderView>(h4_packet.GetHciSpan());
Expand Down
1 change: 1 addition & 0 deletions pw_bluetooth_proxy/l2cap_channel_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ uint16_t L2capChannelManager::GetH4BuffSize() const {
void L2capChannelManager::DrainWriteChannelQueues() {
write_channels_mutex_.lock();

// TODO: https://pwbug.dev/379172336 - Select correct Credits to check.
if (write_channels_.empty() ||
acl_data_channel_.GetNumFreeLeAclPackets() == 0) {
write_channels_mutex_.unlock();
Expand Down
38 changes: 31 additions & 7 deletions pw_bluetooth_proxy/proxy_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ namespace pw::bluetooth::proxy {
ProxyHost::ProxyHost(
pw::Function<void(H4PacketWithHci&& packet)>&& send_to_host_fn,
pw::Function<void(H4PacketWithH4&& packet)>&& send_to_controller_fn,
uint16_t le_acl_credits_to_reserve)
uint16_t le_acl_credits_to_reserve,
uint16_t br_edr_acl_credits_to_reserve)
: hci_transport_(std::move(send_to_host_fn),
std::move(send_to_controller_fn)),
acl_data_channel_(
hci_transport_, l2cap_channel_manager_, le_acl_credits_to_reserve),
acl_data_channel_(hci_transport_,
l2cap_channel_manager_,
le_acl_credits_to_reserve,
br_edr_acl_credits_to_reserve),
l2cap_channel_manager_(acl_data_channel_) {}

ProxyHost::~ProxyHost() { acl_data_channel_.Reset(); }
Expand Down Expand Up @@ -185,6 +188,19 @@ void ProxyHost::HandleCommandCompleteEvent(H4PacketWithHci&& h4_packet) {
PW_MODIFY_DIAGNOSTICS_PUSH();
PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
switch (command_complete_event->command_opcode_enum().Read()) {
case emboss::OpCode::READ_BUFFER_SIZE: {
Result<emboss::ReadBufferSizeCommandCompleteEventWriter> read_event =
MakeEmbossWriter<emboss::ReadBufferSizeCommandCompleteEventWriter>(
hci_buffer);
if (!read_event.ok()) {
PW_LOG_ERROR(
"Buffer is too small for READ_BUFFER_SIZE command complete event. "
"Will not process.");
break;
}
acl_data_channel_.ProcessReadBufferSizeCommandCompleteEvent(*read_event);
break;
}
case emboss::OpCode::LE_READ_BUFFER_SIZE_V1: {
Result<emboss::LEReadBufferSizeV1CommandCompleteEventWriter> read_event =
MakeEmbossWriter<
Expand All @@ -193,7 +209,7 @@ void ProxyHost::HandleCommandCompleteEvent(H4PacketWithHci&& h4_packet) {
PW_LOG_ERROR(
"Buffer is too small for LE_READ_BUFFER_SIZE_V1 command complete "
"event. So will not process.");
return;
break;
}
acl_data_channel_.ProcessLEReadBufferSizeCommandCompleteEvent(
*read_event);
Expand All @@ -207,7 +223,7 @@ void ProxyHost::HandleCommandCompleteEvent(H4PacketWithHci&& h4_packet) {
PW_LOG_ERROR(
"Buffer is too small for LE_READ_BUFFER_SIZE_V2 command complete "
"event. So will not process.");
return;
break;
}
acl_data_channel_.ProcessLEReadBufferSizeCommandCompleteEvent(
*read_event);
Expand Down Expand Up @@ -261,12 +277,20 @@ pw::Status ProxyHost::SendGattNotify(uint16_t connection_handle,
return channel_result->Write(attribute_value);
}

bool ProxyHost::HasSendAclCapability() const {
return acl_data_channel_.GetLeAclCreditsToReserve() > 0;
bool ProxyHost::HasSendLeAclCapability() const {
return acl_data_channel_.HasSendLeAclCapability();
}

bool ProxyHost::HasSendBrEdrAclCapability() const {
return acl_data_channel_.HasSendBrEdrAclCapability();
}

uint16_t ProxyHost::GetNumFreeLeAclPackets() const {
return acl_data_channel_.GetNumFreeLeAclPackets();
}

uint16_t ProxyHost::GetNumFreeBrEdrAclPackets() const {
return acl_data_channel_.GetNumFreeBrEdrAclPackets();
}

} // namespace pw::bluetooth::proxy
Loading

0 comments on commit aeecb42

Please sign in to comment.