Loading system/gd/hci/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ filegroup { "hci_packets_test.cc", "le_advertising_manager_test.cc", "le_scanning_manager_test.cc", "round_robin_scheduler_test.cc", ], } Loading system/gd/hci/round_robin_scheduler.cc +31 −18 Original line number Diff line number Diff line Loading @@ -31,11 +31,11 @@ RoundRobinScheduler::RoundRobinScheduler(os::Handler* handler, Controller* contr le_acl_packet_credits_ = le_max_acl_packet_credits_; le_hci_mtu_ = le_buffer_size.le_data_packet_length_; controller_->RegisterCompletedAclPacketsCallback( common::Bind(&RoundRobinScheduler::IncomingAclCredits, common::Unretained(this)), handler_); common::Bind(&RoundRobinScheduler::incoming_acl_credits, common::Unretained(this)), handler_); } RoundRobinScheduler::~RoundRobinScheduler() { UnregisterAllConnections(); unregister_all_connections(); controller_->UnregisterCompletedAclPacketsCallback(); } Loading @@ -44,7 +44,7 @@ void RoundRobinScheduler::Register(ConnectionType connection_type, uint16_t hand acl_queue_handler acl_queue_handler = {connection_type, queue_down_end, false, 0, false}; acl_queue_handlers_.insert(std::pair<uint16_t, RoundRobinScheduler::acl_queue_handler>(handle, acl_queue_handler)); if (fragments_to_send_.size() == 0) { StartRoundRobin(); start_round_robin(); } } Loading @@ -71,12 +71,20 @@ void RoundRobinScheduler::SetDisconnect(uint16_t handle) { acl_queue_handler.number_of_sent_packets_ = 0; } void RoundRobinScheduler::StartRoundRobin() { uint16_t RoundRobinScheduler::GetCredits() { return acl_packet_credits_; } uint16_t RoundRobinScheduler::GetLeCredits() { return le_acl_packet_credits_; } void RoundRobinScheduler::start_round_robin() { if (acl_packet_credits_ == 0 && le_acl_packet_credits_ == 0) { return; } if (!fragments_to_send_.empty()) { SendNextFragment(); send_next_fragment(); return; } Loading @@ -86,10 +94,15 @@ void RoundRobinScheduler::StartRoundRobin() { size_t count = acl_queue_handlers_.size(); for (auto acl_queue_handler = starting_point_; count > 0; count--) { if (!acl_queue_handler->second.dequeue_is_registered_) { // Prevent registration when credits is zero bool classic_buffer_full = acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::CLASSIC; bool le_buffer_full = le_acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::LE; if (!acl_queue_handler->second.dequeue_is_registered_ && !classic_buffer_full && !le_buffer_full) { acl_queue_handler->second.dequeue_is_registered_ = true; acl_queue_handler->second.queue_down_end_->RegisterDequeue( handler_, common::Bind(&RoundRobinScheduler::BufferPacket, common::Unretained(this), acl_queue_handler)); handler_, common::Bind(&RoundRobinScheduler::buffer_packet, common::Unretained(this), acl_queue_handler)); } acl_queue_handler = std::next(acl_queue_handler); if (acl_queue_handler == acl_queue_handlers_.end()) { Loading @@ -100,7 +113,7 @@ void RoundRobinScheduler::StartRoundRobin() { starting_point_ = std::next(starting_point_); } void RoundRobinScheduler::BufferPacket(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) { void RoundRobinScheduler::buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) { BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT; // Wrap packet and enqueue it uint16_t handle = acl_queue_handler->first; Loading @@ -124,13 +137,13 @@ void RoundRobinScheduler::BufferPacket(std::map<uint16_t, acl_queue_handler>::it } } ASSERT(fragments_to_send_.size() > 0); UnregisterAllConnections(); unregister_all_connections(); acl_queue_handler->second.number_of_sent_packets_ += fragments_to_send_.size(); SendNextFragment(); send_next_fragment(); } void RoundRobinScheduler::UnregisterAllConnections() { void RoundRobinScheduler::unregister_all_connections() { for (auto acl_queue_handler = acl_queue_handlers_.begin(); acl_queue_handler != acl_queue_handlers_.end(); acl_queue_handler = std::next(acl_queue_handler)) { if (acl_queue_handler->second.dequeue_is_registered_) { Loading @@ -140,15 +153,15 @@ void RoundRobinScheduler::UnregisterAllConnections() { } } void RoundRobinScheduler::SendNextFragment() { void RoundRobinScheduler::send_next_fragment() { if (!enqueue_registered_.exchange(true)) { hci_queue_end_->RegisterEnqueue( handler_, common::Bind(&RoundRobinScheduler::HandleEnqueueNextFragment, common::Unretained(this))); handler_, common::Bind(&RoundRobinScheduler::handle_enqueue_next_fragment, common::Unretained(this))); } } // Invoked from some external Queue Reactable context 1 std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment() { std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::handle_enqueue_next_fragment() { ConnectionType connection_type = fragments_to_send_.front().first; if (connection_type == ConnectionType::CLASSIC) { ASSERT(acl_packet_credits_ > 0); Loading @@ -164,7 +177,7 @@ std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment if (enqueue_registered_.exchange(false)) { hci_queue_end_->UnregisterEnqueue(); } handler_->Post(common::BindOnce(&RoundRobinScheduler::StartRoundRobin, common::Unretained(this))); handler_->Post(common::BindOnce(&RoundRobinScheduler::start_round_robin, common::Unretained(this))); } else { ConnectionType next_connection_type = fragments_to_send_.front().first; bool classic_buffer_full = next_connection_type == ConnectionType::CLASSIC && acl_packet_credits_ == 0; Loading @@ -176,7 +189,7 @@ std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment return std::unique_ptr<AclPacketBuilder>(raw_pointer); } void RoundRobinScheduler::IncomingAclCredits(uint16_t handle, uint16_t credits) { void RoundRobinScheduler::incoming_acl_credits(uint16_t handle, uint16_t credits) { auto acl_queue_handler = acl_queue_handlers_.find(handle); if (acl_queue_handler == acl_queue_handlers_.end()) { LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle); Loading @@ -195,7 +208,7 @@ void RoundRobinScheduler::IncomingAclCredits(uint16_t handle, uint16_t credits) ASSERT(acl_packet_credits_ <= max_acl_packet_credits_); ASSERT(le_acl_packet_credits_ <= le_max_acl_packet_credits_); if (acl_packet_credits_ == credits || le_acl_packet_credits_ == credits) { StartRoundRobin(); start_round_robin(); } } Loading system/gd/hci/round_robin_scheduler.h +8 −6 Original line number Diff line number Diff line Loading @@ -46,14 +46,16 @@ class RoundRobinScheduler { void Register(ConnectionType connection_type, uint16_t handle, AclConnection::QueueDownEnd* queue_down_end); void Unregister(uint16_t handle); void SetDisconnect(uint16_t handle); uint16_t GetCredits(); uint16_t GetLeCredits(); private: void StartRoundRobin(); void BufferPacket(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler); void UnregisterAllConnections(); void SendNextFragment(); std::unique_ptr<AclPacketBuilder> HandleEnqueueNextFragment(); void IncomingAclCredits(uint16_t handle, uint16_t credits); void start_round_robin(); void buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler); void unregister_all_connections(); void send_next_fragment(); std::unique_ptr<AclPacketBuilder> handle_enqueue_next_fragment(); void incoming_acl_credits(uint16_t handle, uint16_t credits); os::Handler* handler_ = nullptr; Controller* controller_ = nullptr; Loading system/gd/hci/round_robin_scheduler_test.cc 0 → 100644 +382 −0 Original line number Diff line number Diff line /* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "hci/round_robin_scheduler.h" #include <gtest/gtest.h> #include "common/bidi_queue.h" #include "common/callback.h" #include "hci/controller.h" #include "hci/hci_packets.h" #include "os/handler.h" #include "os/log.h" #include "packet/raw_builder.h" using ::bluetooth::common::BidiQueue; using ::bluetooth::common::Callback; using ::bluetooth::os::Handler; using ::bluetooth::os::Thread; namespace bluetooth { namespace hci { class TestController : public Controller { public: uint16_t GetControllerNumAclPacketBuffers() const { return max_acl_packet_credits_; } uint16_t GetControllerAclPacketLength() const { return hci_mtu_; } LeBufferSize GetControllerLeBufferSize() const { LeBufferSize le_buffer_size; le_buffer_size.le_data_packet_length_ = le_hci_mtu_; le_buffer_size.total_num_le_packets_ = le_max_acl_packet_credits_; return le_buffer_size; } void RegisterCompletedAclPacketsCallback(common::Callback<void(uint16_t /* handle */, uint16_t /* num_packets */)> cb, os::Handler* handler) { acl_credits_handler_ = handler; acl_credits_callback_ = cb; } std::future<void> SendCompletedAclPacketsCallback(uint16_t handle, uint16_t credits) { auto promise = std::make_unique<std::promise<void>>(); auto future = promise->get_future(); acl_credits_handler_->Post(Bind(acl_credits_callback_, handle, credits)); acl_credits_handler_->Post(common::BindOnce( [](std::unique_ptr<std::promise<void>> promise) mutable { promise->set_value(); }, std::move(promise))); return future; } void UnregisterCompletedAclPacketsCallback() { acl_credits_handler_ = nullptr; acl_credits_callback_ = {}; } const uint16_t max_acl_packet_credits_ = 10; const uint16_t hci_mtu_ = 1024; const uint16_t le_max_acl_packet_credits_ = 15; const uint16_t le_hci_mtu_ = 27; private: Handler* acl_credits_handler_; Callback<void(uint16_t, uint16_t)> acl_credits_callback_; }; class RoundRobinSchedulerTest : public ::testing::Test { public: void SetUp() override { thread_ = new Thread("thread", Thread::Priority::NORMAL); handler_ = new Handler(thread_); controller_ = new TestController(); round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_queue_.GetUpEnd()); hci_queue_.GetDownEnd()->RegisterDequeue( handler_, common::Bind(&RoundRobinSchedulerTest::HciDownEndDequeue, common::Unretained(this))); } void TearDown() override { hci_queue_.GetDownEnd()->UnregisterDequeue(); delete round_robin_scheduler_; delete controller_; handler_->Clear(); delete handler_; delete thread_; } void EnqueueAclUpEnd(AclConnection::QueueUpEnd* queue_up_end, std::vector<uint8_t> packet) { if (enqueue_promise_ != nullptr) { enqueue_future_->wait(); } enqueue_promise_ = std::make_unique<std::promise<void>>(); enqueue_future_ = std::make_unique<std::future<void>>(enqueue_promise_->get_future()); queue_up_end->RegisterEnqueue(handler_, common::Bind(&RoundRobinSchedulerTest::enqueue_callback, common::Unretained(this), queue_up_end, packet)); } std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(AclConnection::QueueUpEnd* queue_up_end, std::vector<uint8_t> packet) { auto packet_one = std::make_unique<packet::RawBuilder>(2000); packet_one->AddOctets(packet); queue_up_end->UnregisterEnqueue(); enqueue_promise_->set_value(); return packet_one; }; void HciDownEndDequeue() { auto packet = hci_queue_.GetDownEnd()->TryDequeue(); // Convert from a Builder to a View auto bytes = std::make_shared<std::vector<uint8_t>>(); bluetooth::packet::BitInserter i(*bytes); bytes->reserve(packet->size()); packet->Serialize(i); auto packet_view = bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(bytes); AclPacketView acl_packet_view = AclPacketView::Create(packet_view); ASSERT(acl_packet_view.IsValid()); PacketView<true> count_view = acl_packet_view.GetPayload(); sent_acl_packets_.push(acl_packet_view); packet_count_--; if (packet_count_ == 0) { packet_promise_->set_value(); packet_promise_ = nullptr; } } void VerifyPacket(uint16_t handle, std::vector<uint8_t> packet) { auto acl_packet_view = sent_acl_packets_.front(); ASSERT_EQ(handle, acl_packet_view.GetHandle()); auto payload = acl_packet_view.GetPayload(); for (size_t i = 0; i < payload.size(); i++) { ASSERT_EQ(payload[i], packet[i]); } sent_acl_packets_.pop(); } void SetPacketFuture(uint16_t count) { ASSERT_LOG(packet_promise_ == nullptr, "Promises, Promises, ... Only one at a time."); packet_count_ = count; packet_promise_ = std::make_unique<std::promise<void>>(); packet_future_ = std::make_unique<std::future<void>>(packet_promise_->get_future()); } BidiQueue<AclPacketView, AclPacketBuilder> hci_queue_{3}; Thread* thread_; Handler* handler_; TestController* controller_; RoundRobinScheduler* round_robin_scheduler_; std::queue<AclPacketView> sent_acl_packets_; uint16_t packet_count_; std::unique_ptr<std::promise<void>> packet_promise_; std::unique_ptr<std::future<void>> packet_future_; std::unique_ptr<std::promise<void>> enqueue_promise_; std::unique_ptr<std::future<void>> enqueue_future_; }; TEST_F(RoundRobinSchedulerTest, startup_teardown) {} TEST_F(RoundRobinSchedulerTest, register_unregister_connection) { uint16_t handle = 0x01; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue.GetDownEnd()); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, buffer_packet) { uint16_t handle = 0x01; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); SetPacketFuture(2); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03}; std::vector<uint8_t> packet2 = {0x04, 0x05, 0x06}; EnqueueAclUpEnd(queue_up_end, packet1); EnqueueAclUpEnd(queue_up_end, packet2); packet_future_->wait(); VerifyPacket(handle, packet1); VerifyPacket(handle, packet2); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, buffer_packet_from_two_connections) { uint16_t handle = 0x01; uint16_t le_handle = 0x02; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue{10}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end = le_connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_queue_down_end); SetPacketFuture(2); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue.GetUpEnd(); std::vector<uint8_t> packet = {0x01, 0x02, 0x03}; std::vector<uint8_t> le_packet = {0x04, 0x05, 0x06}; EnqueueAclUpEnd(le_queue_up_end, le_packet); EnqueueAclUpEnd(queue_up_end, packet); packet_future_->wait(); VerifyPacket(le_handle, le_packet); VerifyPacket(handle, packet); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 1); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 1); round_robin_scheduler_->Unregister(handle); round_robin_scheduler_->Unregister(le_handle); } TEST_F(RoundRobinSchedulerTest, do_not_register_when_credits_is_zero) { uint16_t handle = 0x01; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{15}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); SetPacketFuture(10); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); for (uint8_t i = 0; i < 15; i++) { std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i}; EnqueueAclUpEnd(queue_up_end, packet); } packet_future_->wait(); for (uint8_t i = 0; i < 10; i++) { std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i}; VerifyPacket(handle, packet); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), 0); SetPacketFuture(5); auto future = controller_->SendCompletedAclPacketsCallback(0x01, 10); future.wait(); packet_future_->wait(); for (uint8_t i = 10; i < 15; i++) { std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i}; VerifyPacket(handle, packet); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), 5); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, reveived_completed_callback_with_unknown_handle) { auto future = controller_->SendCompletedAclPacketsCallback(0x00, 1); future.wait(); EXPECT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_); EXPECT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_); } TEST_F(RoundRobinSchedulerTest, buffer_packet_intervally) { uint16_t handle1 = 0x01; uint16_t handle2 = 0x02; uint16_t le_handle1 = 0x03; uint16_t le_handle2 = 0x04; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue1{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue2{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue1{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue2{10}; AclConnection::QueueDownEnd* queue_down_end1 = connection_queue1.GetDownEnd(); AclConnection::QueueDownEnd* queue_down_end2 = connection_queue2.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end1 = le_connection_queue1.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end2 = le_connection_queue2.GetDownEnd(); SetPacketFuture(18); AclConnection::QueueUpEnd* queue_up_end1 = connection_queue1.GetUpEnd(); AclConnection::QueueUpEnd* queue_up_end2 = connection_queue2.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end1 = le_connection_queue1.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end2 = le_connection_queue2.GetUpEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle1, queue_down_end1); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle2, queue_down_end2); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle1, le_queue_down_end1); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle2, le_queue_down_end2); std::vector<uint8_t> packet = {0x01, 0x02, 0x03}; EnqueueAclUpEnd(queue_up_end1, packet); EnqueueAclUpEnd(le_queue_up_end2, packet); for (uint8_t i = 0; i < 4; i++) { std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i}; std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i}; std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i}; std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i}; EnqueueAclUpEnd(queue_up_end1, packet1); EnqueueAclUpEnd(queue_up_end2, packet2); EnqueueAclUpEnd(le_queue_up_end1, le_packet1); EnqueueAclUpEnd(le_queue_up_end2, le_packet2); } packet_future_->wait(); VerifyPacket(handle1, packet); VerifyPacket(le_handle2, packet); for (uint8_t i = 0; i < 4; i++) { std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i}; std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i}; std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i}; std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i}; VerifyPacket(handle1, packet1); VerifyPacket(handle2, packet2); VerifyPacket(le_handle1, le_packet1); VerifyPacket(le_handle2, le_packet2); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 9); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 9); round_robin_scheduler_->Unregister(handle1); round_robin_scheduler_->Unregister(handle2); round_robin_scheduler_->Unregister(le_handle1); round_robin_scheduler_->Unregister(le_handle2); } TEST_F(RoundRobinSchedulerTest, send_fragments_without_interval) { uint16_t handle = 0x01; uint16_t le_handle = 0x02; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue{10}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end = le_connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_queue_down_end); SetPacketFuture(5); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue.GetUpEnd(); std::vector<uint8_t> packet(controller_->hci_mtu_, 0xff); std::vector<uint8_t> packet_part1(controller_->hci_mtu_, 0xff); std::vector<uint8_t> packet_part2 = {0x03, 0x02, 0x01}; packet.insert(packet.end(), packet_part2.begin(), packet_part2.end()); std::vector<uint8_t> le_packet; std::vector<uint8_t> le_packet_part1; std::vector<uint8_t> le_packet_part2; std::vector<uint8_t> le_packet_part3; for (uint8_t i = 0; i < controller_->le_hci_mtu_; i++) { le_packet.push_back(i); le_packet_part1.push_back(i); le_packet_part2.push_back(i * 2); le_packet_part3.push_back(i * 3); } le_packet.insert(le_packet.end(), le_packet_part2.begin(), le_packet_part2.end()); le_packet.insert(le_packet.end(), le_packet_part3.begin(), le_packet_part3.end()); EnqueueAclUpEnd(le_queue_up_end, le_packet); EnqueueAclUpEnd(queue_up_end, packet); packet_future_->wait(); VerifyPacket(le_handle, le_packet_part1); VerifyPacket(le_handle, le_packet_part2); VerifyPacket(le_handle, le_packet_part3); VerifyPacket(handle, packet_part1); VerifyPacket(handle, packet_part2); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 3); round_robin_scheduler_->Unregister(handle); round_robin_scheduler_->Unregister(le_handle); } } // namespace hci } // namespace bluetooth No newline at end of file Loading
system/gd/hci/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ filegroup { "hci_packets_test.cc", "le_advertising_manager_test.cc", "le_scanning_manager_test.cc", "round_robin_scheduler_test.cc", ], } Loading
system/gd/hci/round_robin_scheduler.cc +31 −18 Original line number Diff line number Diff line Loading @@ -31,11 +31,11 @@ RoundRobinScheduler::RoundRobinScheduler(os::Handler* handler, Controller* contr le_acl_packet_credits_ = le_max_acl_packet_credits_; le_hci_mtu_ = le_buffer_size.le_data_packet_length_; controller_->RegisterCompletedAclPacketsCallback( common::Bind(&RoundRobinScheduler::IncomingAclCredits, common::Unretained(this)), handler_); common::Bind(&RoundRobinScheduler::incoming_acl_credits, common::Unretained(this)), handler_); } RoundRobinScheduler::~RoundRobinScheduler() { UnregisterAllConnections(); unregister_all_connections(); controller_->UnregisterCompletedAclPacketsCallback(); } Loading @@ -44,7 +44,7 @@ void RoundRobinScheduler::Register(ConnectionType connection_type, uint16_t hand acl_queue_handler acl_queue_handler = {connection_type, queue_down_end, false, 0, false}; acl_queue_handlers_.insert(std::pair<uint16_t, RoundRobinScheduler::acl_queue_handler>(handle, acl_queue_handler)); if (fragments_to_send_.size() == 0) { StartRoundRobin(); start_round_robin(); } } Loading @@ -71,12 +71,20 @@ void RoundRobinScheduler::SetDisconnect(uint16_t handle) { acl_queue_handler.number_of_sent_packets_ = 0; } void RoundRobinScheduler::StartRoundRobin() { uint16_t RoundRobinScheduler::GetCredits() { return acl_packet_credits_; } uint16_t RoundRobinScheduler::GetLeCredits() { return le_acl_packet_credits_; } void RoundRobinScheduler::start_round_robin() { if (acl_packet_credits_ == 0 && le_acl_packet_credits_ == 0) { return; } if (!fragments_to_send_.empty()) { SendNextFragment(); send_next_fragment(); return; } Loading @@ -86,10 +94,15 @@ void RoundRobinScheduler::StartRoundRobin() { size_t count = acl_queue_handlers_.size(); for (auto acl_queue_handler = starting_point_; count > 0; count--) { if (!acl_queue_handler->second.dequeue_is_registered_) { // Prevent registration when credits is zero bool classic_buffer_full = acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::CLASSIC; bool le_buffer_full = le_acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::LE; if (!acl_queue_handler->second.dequeue_is_registered_ && !classic_buffer_full && !le_buffer_full) { acl_queue_handler->second.dequeue_is_registered_ = true; acl_queue_handler->second.queue_down_end_->RegisterDequeue( handler_, common::Bind(&RoundRobinScheduler::BufferPacket, common::Unretained(this), acl_queue_handler)); handler_, common::Bind(&RoundRobinScheduler::buffer_packet, common::Unretained(this), acl_queue_handler)); } acl_queue_handler = std::next(acl_queue_handler); if (acl_queue_handler == acl_queue_handlers_.end()) { Loading @@ -100,7 +113,7 @@ void RoundRobinScheduler::StartRoundRobin() { starting_point_ = std::next(starting_point_); } void RoundRobinScheduler::BufferPacket(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) { void RoundRobinScheduler::buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) { BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT; // Wrap packet and enqueue it uint16_t handle = acl_queue_handler->first; Loading @@ -124,13 +137,13 @@ void RoundRobinScheduler::BufferPacket(std::map<uint16_t, acl_queue_handler>::it } } ASSERT(fragments_to_send_.size() > 0); UnregisterAllConnections(); unregister_all_connections(); acl_queue_handler->second.number_of_sent_packets_ += fragments_to_send_.size(); SendNextFragment(); send_next_fragment(); } void RoundRobinScheduler::UnregisterAllConnections() { void RoundRobinScheduler::unregister_all_connections() { for (auto acl_queue_handler = acl_queue_handlers_.begin(); acl_queue_handler != acl_queue_handlers_.end(); acl_queue_handler = std::next(acl_queue_handler)) { if (acl_queue_handler->second.dequeue_is_registered_) { Loading @@ -140,15 +153,15 @@ void RoundRobinScheduler::UnregisterAllConnections() { } } void RoundRobinScheduler::SendNextFragment() { void RoundRobinScheduler::send_next_fragment() { if (!enqueue_registered_.exchange(true)) { hci_queue_end_->RegisterEnqueue( handler_, common::Bind(&RoundRobinScheduler::HandleEnqueueNextFragment, common::Unretained(this))); handler_, common::Bind(&RoundRobinScheduler::handle_enqueue_next_fragment, common::Unretained(this))); } } // Invoked from some external Queue Reactable context 1 std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment() { std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::handle_enqueue_next_fragment() { ConnectionType connection_type = fragments_to_send_.front().first; if (connection_type == ConnectionType::CLASSIC) { ASSERT(acl_packet_credits_ > 0); Loading @@ -164,7 +177,7 @@ std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment if (enqueue_registered_.exchange(false)) { hci_queue_end_->UnregisterEnqueue(); } handler_->Post(common::BindOnce(&RoundRobinScheduler::StartRoundRobin, common::Unretained(this))); handler_->Post(common::BindOnce(&RoundRobinScheduler::start_round_robin, common::Unretained(this))); } else { ConnectionType next_connection_type = fragments_to_send_.front().first; bool classic_buffer_full = next_connection_type == ConnectionType::CLASSIC && acl_packet_credits_ == 0; Loading @@ -176,7 +189,7 @@ std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment return std::unique_ptr<AclPacketBuilder>(raw_pointer); } void RoundRobinScheduler::IncomingAclCredits(uint16_t handle, uint16_t credits) { void RoundRobinScheduler::incoming_acl_credits(uint16_t handle, uint16_t credits) { auto acl_queue_handler = acl_queue_handlers_.find(handle); if (acl_queue_handler == acl_queue_handlers_.end()) { LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle); Loading @@ -195,7 +208,7 @@ void RoundRobinScheduler::IncomingAclCredits(uint16_t handle, uint16_t credits) ASSERT(acl_packet_credits_ <= max_acl_packet_credits_); ASSERT(le_acl_packet_credits_ <= le_max_acl_packet_credits_); if (acl_packet_credits_ == credits || le_acl_packet_credits_ == credits) { StartRoundRobin(); start_round_robin(); } } Loading
system/gd/hci/round_robin_scheduler.h +8 −6 Original line number Diff line number Diff line Loading @@ -46,14 +46,16 @@ class RoundRobinScheduler { void Register(ConnectionType connection_type, uint16_t handle, AclConnection::QueueDownEnd* queue_down_end); void Unregister(uint16_t handle); void SetDisconnect(uint16_t handle); uint16_t GetCredits(); uint16_t GetLeCredits(); private: void StartRoundRobin(); void BufferPacket(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler); void UnregisterAllConnections(); void SendNextFragment(); std::unique_ptr<AclPacketBuilder> HandleEnqueueNextFragment(); void IncomingAclCredits(uint16_t handle, uint16_t credits); void start_round_robin(); void buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler); void unregister_all_connections(); void send_next_fragment(); std::unique_ptr<AclPacketBuilder> handle_enqueue_next_fragment(); void incoming_acl_credits(uint16_t handle, uint16_t credits); os::Handler* handler_ = nullptr; Controller* controller_ = nullptr; Loading
system/gd/hci/round_robin_scheduler_test.cc 0 → 100644 +382 −0 Original line number Diff line number Diff line /* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "hci/round_robin_scheduler.h" #include <gtest/gtest.h> #include "common/bidi_queue.h" #include "common/callback.h" #include "hci/controller.h" #include "hci/hci_packets.h" #include "os/handler.h" #include "os/log.h" #include "packet/raw_builder.h" using ::bluetooth::common::BidiQueue; using ::bluetooth::common::Callback; using ::bluetooth::os::Handler; using ::bluetooth::os::Thread; namespace bluetooth { namespace hci { class TestController : public Controller { public: uint16_t GetControllerNumAclPacketBuffers() const { return max_acl_packet_credits_; } uint16_t GetControllerAclPacketLength() const { return hci_mtu_; } LeBufferSize GetControllerLeBufferSize() const { LeBufferSize le_buffer_size; le_buffer_size.le_data_packet_length_ = le_hci_mtu_; le_buffer_size.total_num_le_packets_ = le_max_acl_packet_credits_; return le_buffer_size; } void RegisterCompletedAclPacketsCallback(common::Callback<void(uint16_t /* handle */, uint16_t /* num_packets */)> cb, os::Handler* handler) { acl_credits_handler_ = handler; acl_credits_callback_ = cb; } std::future<void> SendCompletedAclPacketsCallback(uint16_t handle, uint16_t credits) { auto promise = std::make_unique<std::promise<void>>(); auto future = promise->get_future(); acl_credits_handler_->Post(Bind(acl_credits_callback_, handle, credits)); acl_credits_handler_->Post(common::BindOnce( [](std::unique_ptr<std::promise<void>> promise) mutable { promise->set_value(); }, std::move(promise))); return future; } void UnregisterCompletedAclPacketsCallback() { acl_credits_handler_ = nullptr; acl_credits_callback_ = {}; } const uint16_t max_acl_packet_credits_ = 10; const uint16_t hci_mtu_ = 1024; const uint16_t le_max_acl_packet_credits_ = 15; const uint16_t le_hci_mtu_ = 27; private: Handler* acl_credits_handler_; Callback<void(uint16_t, uint16_t)> acl_credits_callback_; }; class RoundRobinSchedulerTest : public ::testing::Test { public: void SetUp() override { thread_ = new Thread("thread", Thread::Priority::NORMAL); handler_ = new Handler(thread_); controller_ = new TestController(); round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_queue_.GetUpEnd()); hci_queue_.GetDownEnd()->RegisterDequeue( handler_, common::Bind(&RoundRobinSchedulerTest::HciDownEndDequeue, common::Unretained(this))); } void TearDown() override { hci_queue_.GetDownEnd()->UnregisterDequeue(); delete round_robin_scheduler_; delete controller_; handler_->Clear(); delete handler_; delete thread_; } void EnqueueAclUpEnd(AclConnection::QueueUpEnd* queue_up_end, std::vector<uint8_t> packet) { if (enqueue_promise_ != nullptr) { enqueue_future_->wait(); } enqueue_promise_ = std::make_unique<std::promise<void>>(); enqueue_future_ = std::make_unique<std::future<void>>(enqueue_promise_->get_future()); queue_up_end->RegisterEnqueue(handler_, common::Bind(&RoundRobinSchedulerTest::enqueue_callback, common::Unretained(this), queue_up_end, packet)); } std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(AclConnection::QueueUpEnd* queue_up_end, std::vector<uint8_t> packet) { auto packet_one = std::make_unique<packet::RawBuilder>(2000); packet_one->AddOctets(packet); queue_up_end->UnregisterEnqueue(); enqueue_promise_->set_value(); return packet_one; }; void HciDownEndDequeue() { auto packet = hci_queue_.GetDownEnd()->TryDequeue(); // Convert from a Builder to a View auto bytes = std::make_shared<std::vector<uint8_t>>(); bluetooth::packet::BitInserter i(*bytes); bytes->reserve(packet->size()); packet->Serialize(i); auto packet_view = bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(bytes); AclPacketView acl_packet_view = AclPacketView::Create(packet_view); ASSERT(acl_packet_view.IsValid()); PacketView<true> count_view = acl_packet_view.GetPayload(); sent_acl_packets_.push(acl_packet_view); packet_count_--; if (packet_count_ == 0) { packet_promise_->set_value(); packet_promise_ = nullptr; } } void VerifyPacket(uint16_t handle, std::vector<uint8_t> packet) { auto acl_packet_view = sent_acl_packets_.front(); ASSERT_EQ(handle, acl_packet_view.GetHandle()); auto payload = acl_packet_view.GetPayload(); for (size_t i = 0; i < payload.size(); i++) { ASSERT_EQ(payload[i], packet[i]); } sent_acl_packets_.pop(); } void SetPacketFuture(uint16_t count) { ASSERT_LOG(packet_promise_ == nullptr, "Promises, Promises, ... Only one at a time."); packet_count_ = count; packet_promise_ = std::make_unique<std::promise<void>>(); packet_future_ = std::make_unique<std::future<void>>(packet_promise_->get_future()); } BidiQueue<AclPacketView, AclPacketBuilder> hci_queue_{3}; Thread* thread_; Handler* handler_; TestController* controller_; RoundRobinScheduler* round_robin_scheduler_; std::queue<AclPacketView> sent_acl_packets_; uint16_t packet_count_; std::unique_ptr<std::promise<void>> packet_promise_; std::unique_ptr<std::future<void>> packet_future_; std::unique_ptr<std::promise<void>> enqueue_promise_; std::unique_ptr<std::future<void>> enqueue_future_; }; TEST_F(RoundRobinSchedulerTest, startup_teardown) {} TEST_F(RoundRobinSchedulerTest, register_unregister_connection) { uint16_t handle = 0x01; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue.GetDownEnd()); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, buffer_packet) { uint16_t handle = 0x01; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); SetPacketFuture(2); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03}; std::vector<uint8_t> packet2 = {0x04, 0x05, 0x06}; EnqueueAclUpEnd(queue_up_end, packet1); EnqueueAclUpEnd(queue_up_end, packet2); packet_future_->wait(); VerifyPacket(handle, packet1); VerifyPacket(handle, packet2); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, buffer_packet_from_two_connections) { uint16_t handle = 0x01; uint16_t le_handle = 0x02; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue{10}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end = le_connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_queue_down_end); SetPacketFuture(2); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue.GetUpEnd(); std::vector<uint8_t> packet = {0x01, 0x02, 0x03}; std::vector<uint8_t> le_packet = {0x04, 0x05, 0x06}; EnqueueAclUpEnd(le_queue_up_end, le_packet); EnqueueAclUpEnd(queue_up_end, packet); packet_future_->wait(); VerifyPacket(le_handle, le_packet); VerifyPacket(handle, packet); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 1); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 1); round_robin_scheduler_->Unregister(handle); round_robin_scheduler_->Unregister(le_handle); } TEST_F(RoundRobinSchedulerTest, do_not_register_when_credits_is_zero) { uint16_t handle = 0x01; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{15}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); SetPacketFuture(10); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); for (uint8_t i = 0; i < 15; i++) { std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i}; EnqueueAclUpEnd(queue_up_end, packet); } packet_future_->wait(); for (uint8_t i = 0; i < 10; i++) { std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i}; VerifyPacket(handle, packet); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), 0); SetPacketFuture(5); auto future = controller_->SendCompletedAclPacketsCallback(0x01, 10); future.wait(); packet_future_->wait(); for (uint8_t i = 10; i < 15; i++) { std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i}; VerifyPacket(handle, packet); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), 5); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, reveived_completed_callback_with_unknown_handle) { auto future = controller_->SendCompletedAclPacketsCallback(0x00, 1); future.wait(); EXPECT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_); EXPECT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_); } TEST_F(RoundRobinSchedulerTest, buffer_packet_intervally) { uint16_t handle1 = 0x01; uint16_t handle2 = 0x02; uint16_t le_handle1 = 0x03; uint16_t le_handle2 = 0x04; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue1{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue2{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue1{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue2{10}; AclConnection::QueueDownEnd* queue_down_end1 = connection_queue1.GetDownEnd(); AclConnection::QueueDownEnd* queue_down_end2 = connection_queue2.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end1 = le_connection_queue1.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end2 = le_connection_queue2.GetDownEnd(); SetPacketFuture(18); AclConnection::QueueUpEnd* queue_up_end1 = connection_queue1.GetUpEnd(); AclConnection::QueueUpEnd* queue_up_end2 = connection_queue2.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end1 = le_connection_queue1.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end2 = le_connection_queue2.GetUpEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle1, queue_down_end1); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle2, queue_down_end2); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle1, le_queue_down_end1); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle2, le_queue_down_end2); std::vector<uint8_t> packet = {0x01, 0x02, 0x03}; EnqueueAclUpEnd(queue_up_end1, packet); EnqueueAclUpEnd(le_queue_up_end2, packet); for (uint8_t i = 0; i < 4; i++) { std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i}; std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i}; std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i}; std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i}; EnqueueAclUpEnd(queue_up_end1, packet1); EnqueueAclUpEnd(queue_up_end2, packet2); EnqueueAclUpEnd(le_queue_up_end1, le_packet1); EnqueueAclUpEnd(le_queue_up_end2, le_packet2); } packet_future_->wait(); VerifyPacket(handle1, packet); VerifyPacket(le_handle2, packet); for (uint8_t i = 0; i < 4; i++) { std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i}; std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i}; std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i}; std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i}; VerifyPacket(handle1, packet1); VerifyPacket(handle2, packet2); VerifyPacket(le_handle1, le_packet1); VerifyPacket(le_handle2, le_packet2); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 9); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 9); round_robin_scheduler_->Unregister(handle1); round_robin_scheduler_->Unregister(handle2); round_robin_scheduler_->Unregister(le_handle1); round_robin_scheduler_->Unregister(le_handle2); } TEST_F(RoundRobinSchedulerTest, send_fragments_without_interval) { uint16_t handle = 0x01; uint16_t le_handle = 0x02; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10}; BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue{10}; AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd(); AclConnection::QueueDownEnd* le_queue_down_end = le_connection_queue.GetDownEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_queue_down_end); SetPacketFuture(5); AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue.GetUpEnd(); std::vector<uint8_t> packet(controller_->hci_mtu_, 0xff); std::vector<uint8_t> packet_part1(controller_->hci_mtu_, 0xff); std::vector<uint8_t> packet_part2 = {0x03, 0x02, 0x01}; packet.insert(packet.end(), packet_part2.begin(), packet_part2.end()); std::vector<uint8_t> le_packet; std::vector<uint8_t> le_packet_part1; std::vector<uint8_t> le_packet_part2; std::vector<uint8_t> le_packet_part3; for (uint8_t i = 0; i < controller_->le_hci_mtu_; i++) { le_packet.push_back(i); le_packet_part1.push_back(i); le_packet_part2.push_back(i * 2); le_packet_part3.push_back(i * 3); } le_packet.insert(le_packet.end(), le_packet_part2.begin(), le_packet_part2.end()); le_packet.insert(le_packet.end(), le_packet_part3.begin(), le_packet_part3.end()); EnqueueAclUpEnd(le_queue_up_end, le_packet); EnqueueAclUpEnd(queue_up_end, packet); packet_future_->wait(); VerifyPacket(le_handle, le_packet_part1); VerifyPacket(le_handle, le_packet_part2); VerifyPacket(le_handle, le_packet_part3); VerifyPacket(handle, packet_part1); VerifyPacket(handle, packet_part2); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 3); round_robin_scheduler_->Unregister(handle); round_robin_scheduler_->Unregister(le_handle); } } // namespace hci } // namespace bluetooth No newline at end of file