diff --git a/pw_bluetooth_proxy/proxy_host.cc b/pw_bluetooth_proxy/proxy_host.cc index ce5c03e92..20bd54aab 100644 --- a/pw_bluetooth_proxy/proxy_host.cc +++ b/pw_bluetooth_proxy/proxy_host.cc @@ -399,26 +399,14 @@ pw::Result ProxyHost::AcquireL2capCoc( L2capCoc::CocConfig tx_config, Function payload)>&& receive_fn, Function&& event_fn, - Function&& queue_space_available_fn, - uint16_t rx_additional_credits) { + Function&& queue_space_available_fn) { Status status = acl_data_channel_.CreateAclConnection(connection_handle, AclTransportType::kLe); if (status.IsResourceExhausted()) { return pw::Status::Unavailable(); } PW_CHECK(status.ok() || status.IsAlreadyExists()); - if (rx_additional_credits > 0) { - L2capSignalingChannel* signaling_channel = - acl_data_channel_.FindSignalingChannel( - connection_handle, - static_cast(emboss::L2capFixedCid::LE_U_SIGNALING)); - PW_CHECK(signaling_channel); - status = signaling_channel->SendFlowControlCreditInd(rx_config.cid, - rx_additional_credits); - if (!status.ok()) { - return status; - } - } + return L2capCocInternal::Create(l2cap_channel_manager_, connection_handle, rx_config, @@ -428,6 +416,20 @@ pw::Result ProxyHost::AcquireL2capCoc( std::move(queue_space_available_fn)); } +pw::Status ProxyHost::SendAdditionalRxCredits(uint16_t connection_handle, + uint16_t local_cid, + uint16_t additional_rx_credits) { + L2capSignalingChannel* signaling_channel = + acl_data_channel_.FindSignalingChannel( + connection_handle, + static_cast(emboss::L2capFixedCid::LE_U_SIGNALING)); + if (!signaling_channel) { + return Status::NotFound(); + } + return signaling_channel->SendFlowControlCreditInd(local_cid, + additional_rx_credits); +} + pw::Result ProxyHost::AcquireBasicL2capChannel( uint16_t connection_handle, uint16_t local_cid, diff --git a/pw_bluetooth_proxy/proxy_host_test.cc b/pw_bluetooth_proxy/proxy_host_test.cc index f9a79d4c2..66e2cab5d 100644 --- a/pw_bluetooth_proxy/proxy_host_test.cc +++ b/pw_bluetooth_proxy/proxy_host_test.cc @@ -3965,7 +3965,7 @@ TEST(L2capSignalingTest, CreditIndAddressedToNonManagedChannelForwardedToHost) { EXPECT_EQ(forwards_to_host, 1); } -TEST(L2capSignalingTest, RxAdditionalCreditsSentOnL2capCocAcquisition) { +TEST(L2capSignalingTest, RxAdditionalCreditsSent) { struct { uint16_t handle = 123; uint16_t local_cid = 456; @@ -4014,11 +4014,13 @@ TEST(L2capSignalingTest, RxAdditionalCreditsSentOnL2capCocAcquisition) { // Allow proxy to reserve 1 LE credit. PW_TEST_EXPECT_OK(SendLeReadBufferResponseFromController(proxy, 1)); - L2capCoc channel = - BuildCoc(proxy, - CocParameters{.handle = capture.handle, - .local_cid = capture.local_cid, - .rx_additional_credits = capture.credits}); + // Build channel so ACL connection is registered. + L2capCoc channel = BuildCoc( + proxy, + CocParameters{.handle = capture.handle, .local_cid = capture.local_cid}); + + PW_TEST_EXPECT_OK(proxy.SendAdditionalRxCredits( + capture.handle, capture.local_cid, capture.credits)); EXPECT_EQ(capture.sends_called, 1); } diff --git a/pw_bluetooth_proxy/public/pw_bluetooth_proxy/proxy_host.h b/pw_bluetooth_proxy/public/pw_bluetooth_proxy/proxy_host.h index f902b789a..4507a7018 100644 --- a/pw_bluetooth_proxy/public/pw_bluetooth_proxy/proxy_host.h +++ b/pw_bluetooth_proxy/public/pw_bluetooth_proxy/proxy_host.h @@ -115,26 +115,22 @@ class ProxyHost { /// Returns an L2CAP connection-oriented channel that supports writing to and /// reading from a remote peer. /// - /// @param[in] connection_handle The connection handle of the remote peer. + /// @param[in] connection_handle The connection handle of the remote peer. /// - /// @param[in] rx_config Parameters applying to reading packets. - /// See `l2cap_coc.h` for details. + /// @param[in] rx_config Parameters applying to reading packets. See + /// `l2cap_coc.h` for details. /// - /// @param[in] tx_config Parameters applying to writing packets. - /// See `l2cap_coc.h` for details. + /// @param[in] tx_config Parameters applying to writing packets. See + /// `l2cap_coc.h` for details. /// - /// @param[in] receive_fn Read callback to be invoked on Rx SDUs. + /// @param[in] receive_fn Read callback to be invoked on Rx SDUs. /// - /// @param[in] event_fn Handle asynchronous events such as errors - /// encountered by the channel. - /// - /// @param[in] rx_additional_credits Send L2CAP_FLOW_CONTROL_CREDIT_IND to - /// dispense remote peer additional credits - /// for this channel. + /// @param[in] event_fn Handle asynchronous events such as errors + /// encountered by the channel. /// /// @param[in] queue_space_available_fn - /// Callback to be invoked after resources become - /// available after an UNAVAILABLE Write. + /// Callback to be invoked after resources + /// become available after an UNAVAILABLE Write. /// /// @returns @rst /// @@ -147,10 +143,31 @@ class ProxyHost { uint16_t connection_handle, L2capCoc::CocConfig rx_config, L2capCoc::CocConfig tx_config, - Function payload)>&& receive_fn, - Function&& event_fn, - Function&& queue_space_available_fn = nullptr, - uint16_t rx_additional_credits = 0); + pw::Function payload)>&& receive_fn, + pw::Function&& event_fn, + Function&& queue_space_available_fn = nullptr); + + /// Send an L2CAP_FLOW_CONTROL_CREDIT_IND signaling packet to dispense the + /// remote peer additional L2CAP connection-oriented channel credits for this + /// channel. + /// + /// @param[in] connection_handle ACL connection over which this L2CAP + /// connection-oriented channel exists. + /// + /// @param[in] local_cid L2CAP channel ID of local endpoint. + /// + /// @param[in] additional_rx_credits Number of credits to dispense. + /// + /// @returns @rst + /// + /// .. pw-status-codes:: + /// INVALID_ARGUMENT: CID invalid (check logs). + /// NOT_FOUND: Requested ACL connection does not exist. + /// UNAVAILABLE: Send could not be queued right now (transient error). + /// @endrst + pw::Status SendAdditionalRxCredits(uint16_t connection_handle, + uint16_t local_cid, + uint16_t additional_rx_credits); /// Returns an L2CAP channel operating in basic mode that supports writing to /// and reading from a remote peer. diff --git a/pw_bluetooth_proxy/pw_bluetooth_proxy_private/test_utils.h b/pw_bluetooth_proxy/pw_bluetooth_proxy_private/test_utils.h index 9f859918d..d4f935d99 100644 --- a/pw_bluetooth_proxy/pw_bluetooth_proxy_private/test_utils.h +++ b/pw_bluetooth_proxy/pw_bluetooth_proxy_private/test_utils.h @@ -182,7 +182,7 @@ struct CocParameters { uint16_t tx_credits = 1; pw::Function payload)>&& receive_fn = nullptr; pw::Function&& event_fn = nullptr; - uint16_t rx_additional_credits = 0; + pw::Function&& queue_space_available_fn = nullptr; }; // Open and return an L2CAP connection-oriented channel managed by `proxy`. diff --git a/pw_bluetooth_proxy/test_utils.cc b/pw_bluetooth_proxy/test_utils.cc index eba99135b..291dd162d 100644 --- a/pw_bluetooth_proxy/test_utils.cc +++ b/pw_bluetooth_proxy/test_utils.cc @@ -300,8 +300,7 @@ L2capCoc BuildCoc(ProxyHost& proxy, CocParameters params) { .credits = params.tx_credits}, std::move(params.receive_fn), std::move(params.event_fn), - /*queue_space_available_fn=*/nullptr, - params.rx_additional_credits); + std::move(params.queue_space_available_fn)); return std::move(channel.value()); }