Loading system/gd/hci/hci_layer.cc +117 −34 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ #include "hci/hci_layer.h" #include "os/alarm.h" #include "common/bind.h" #include "common/callback.h" Loading Loading @@ -45,12 +46,17 @@ class EventHandler { class CommandQueueEntry { public: CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet, OnceCallback<void(CommandStatusView)> on_status_function, OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler) : command(std::move(command_packet)), on_status(std::move(on_status_function)), on_complete(std::move(on_complete_function)), caller_handler(handler) {} : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)), caller_handler(handler) {} CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet, OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler) : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)), caller_handler(handler) {} std::unique_ptr<CommandPacketBuilder> command; bool waiting_for_status_; OnceCallback<void(CommandStatusView)> on_status; OnceCallback<void(CommandCompleteView)> on_complete; Handler* caller_handler; Loading @@ -63,13 +69,32 @@ namespace hci { using common::Address; using common::BidiQueue; using common::BidiQueueEnd; using os::Alarm; using os::Handler; namespace { using hci::OpCode; using hci::ResetCompleteView; void fail_if_reset_complete_not_success(CommandCompleteView complete) { auto reset_complete = ResetCompleteView::Create(complete); ASSERT(reset_complete.IsValid()); ASSERT(reset_complete.GetStatus() == ErrorCode::SUCCESS); } void on_hci_timeout(OpCode op_code) { ASSERT_LOG(false, "Timed out waiting for 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str()); } } // namespace struct HciLayer::impl : public hal::HciHalCallbacks { impl(HciLayer& module) : hal_(nullptr), module_(module) {} ~impl() {} void Start(hal::HciHal* hal) { hal_ = hal; hci_timeout_alarm_ = new Alarm(module_.GetHandler()); auto queue_end = acl_queue_.GetDownEnd(); Handler* handler = module_.GetHandler(); Loading @@ -78,6 +103,7 @@ struct HciLayer::impl : public hal::HciHalCallbacks { handler); RegisterEventHandler(EventCode::COMMAND_STATUS, Bind(&impl::command_status_callback, common::Unretained(this)), handler); EnqueueCommand(ResetBuilder::Create(), BindOnce(&fail_if_reset_complete_not_success), handler); hal_->registerIncomingPacketCallback(this); } Loading @@ -86,6 +112,13 @@ struct HciLayer::impl : public hal::HciHalCallbacks { send_acl(std::move(packet)); } void Stop() { acl_queue_.GetDownEnd()->UnregisterDequeue(); delete hci_timeout_alarm_; command_queue_.clear(); hal_ = nullptr; } void send_acl(std::unique_ptr<hci::BasePacketBuilder> packet) { std::vector<uint8_t> bytes; BitInserter bi(bytes); Loading @@ -100,37 +133,52 @@ struct HciLayer::impl : public hal::HciHalCallbacks { hal_->sendScoData(bytes); } void Stop() { acl_queue_.GetDownEnd()->UnregisterDequeue(); hal_ = nullptr; } void command_status_callback(EventPacketView event) { CommandStatusView status_view = CommandStatusView::Create(event); ASSERT(status_view.IsValid()); if (command_queue_.size() == 0) { ASSERT_LOG(status_view.GetCommandOpCode() == OpCode::NONE, "Unexpected status event with OpCode 0x%02hx", status_view.GetCommandOpCode()); command_credits_ = status_view.GetNumHciCommandPackets(); OpCode op_code = status_view.GetCommandOpCode(); if (op_code == OpCode::NONE) { send_next_command(); return; } // TODO: Check whether this is the CommandOpCode we're looking for. ASSERT_LOG(!command_queue_.empty(), "Unexpected status event with OpCode 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(command_queue_.front().waiting_for_status_, "Waiting for command complete 0x%02hx (%s), got command status for 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); auto caller_handler = command_queue_.front().caller_handler; caller_handler->Post(BindOnce(std::move(command_queue_.front().on_status), std::move(status_view))); command_queue_.pop(); command_queue_.pop_front(); waiting_command_ = OpCode::NONE; hci_timeout_alarm_->Cancel(); send_next_command(); } void command_complete_callback(EventPacketView event) { CommandCompleteView complete_view = CommandCompleteView::Create(event); ASSERT(complete_view.IsValid()); if (command_queue_.size() == 0) { ASSERT_LOG(complete_view.GetCommandOpCode() == OpCode::NONE, "Unexpected command complete event with OpCode 0x%02hx", complete_view.GetCommandOpCode()); command_credits_ = complete_view.GetNumHciCommandPackets(); OpCode op_code = complete_view.GetCommandOpCode(); if (op_code == OpCode::NONE) { send_next_command(); return; } // TODO: Check whether this is the CommandOpCode we're looking for. ASSERT_LOG(command_queue_.size() > 0, "Unexpected command complete with OpCode 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(!command_queue_.front().waiting_for_status_, "Waiting for command status 0x%02hx (%s), got command complete for 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); auto caller_handler = command_queue_.front().caller_handler; caller_handler->Post(BindOnce(std::move(command_queue_.front().on_complete), std::move(complete_view))); command_queue_.pop(); command_queue_.pop_front(); waiting_command_ = OpCode::NONE; hci_timeout_alarm_->Cancel(); send_next_command(); } void hciEventReceived(hal::HciPacket event_bytes) override { Loading @@ -147,7 +195,6 @@ struct HciLayer::impl : public hal::HciHalCallbacks { event.GetEventCode()); auto& registered_handler = event_handlers_[event_code].event_handler; event_handlers_[event_code].handler->Post(BindOnce(registered_handler, std::move(event))); // TODO: Credits } void aclDataReceived(hal::HciPacket data_bytes) override { Loading @@ -174,24 +221,53 @@ struct HciLayer::impl : public hal::HciHalCallbacks { ScoPacketView sco = ScoPacketView::Create(packet); } void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command, common::Unretained(this), std::move(command), std::move(on_status), std::move(on_complete), module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_complete, common::Unretained(this), std::move(command), std::move(on_complete), common::Unretained(handler))); } void handle_enqueue_command(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_status, common::Unretained(this), std::move(command), std::move(on_status), common::Unretained(handler))); } void handle_enqueue_command_with_complete(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { command_queue_.emplace(std::move(command), std::move(on_status), std::move(on_complete), handler); command_queue_.emplace_back(std::move(command), std::move(on_complete), handler); if (command_queue_.size() == 1) { std::vector<uint8_t> bytes; BitInserter bi(bytes); command_queue_.front().command->Serialize(bi); hal_->sendHciCommand(bytes); send_next_command(); } void handle_enqueue_command_with_status(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { command_queue_.emplace_back(std::move(command), std::move(on_status), handler); send_next_command(); } void send_next_command() { if (command_credits_ == 0) { return; } if (waiting_command_ != OpCode::NONE) { return; } if (command_queue_.size() == 0) { return; } std::shared_ptr<std::vector<uint8_t>> bytes = std::make_shared<std::vector<uint8_t>>(); BitInserter bi(*bytes); command_queue_.front().command->Serialize(bi); hal_->sendHciCommand(*bytes); auto cmd_view = CommandPacketView::Create(bytes); ASSERT(cmd_view.IsValid()); OpCode op_code = cmd_view.GetOpCode(); waiting_command_ = op_code; command_credits_ = 0; // Only allow one outstanding command hci_timeout_alarm_->Schedule(BindOnce(&on_hci_timeout, op_code), kHciTimeoutMs); } BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd() { Loading Loading @@ -227,9 +303,12 @@ struct HciLayer::impl : public hal::HciHalCallbacks { HciLayer& module_; // Command Handling std::queue<CommandQueueEntry> command_queue_; std::list<CommandQueueEntry> command_queue_; std::map<EventCode, EventHandler> event_handlers_; OpCode waiting_command_{OpCode::NONE}; uint8_t command_credits_{1}; // Send reset first Alarm* hci_timeout_alarm_{nullptr}; // Acl packets BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */}; Loading @@ -242,9 +321,13 @@ HciLayer::~HciLayer() { } void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { impl_->EnqueueCommand(std::move(command), std::move(on_status), std::move(on_complete), handler); impl_->EnqueueCommand(std::move(command), std::move(on_complete), handler); } void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { impl_->EnqueueCommand(std::move(command), std::move(on_status), handler); } common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* HciLayer::GetAclQueueEnd() { Loading system/gd/hci/hci_layer.h +5 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <chrono> #include <map> #include "common/address.h" Loading @@ -37,9 +38,11 @@ class HciLayer : public Module { DISALLOW_COPY_AND_ASSIGN(HciLayer); virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler); virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler); virtual common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd(); virtual void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler, Loading @@ -54,6 +57,7 @@ class HciLayer : public Module { void Start() override; void Stop() override; static constexpr std::chrono::milliseconds kHciTimeoutMs = std::chrono::milliseconds(2000); private: struct impl; Loading system/gd/hci/hci_layer_test.cc +149 −9 Original line number Diff line number Diff line Loading @@ -75,12 +75,16 @@ class TestHciHal : public hal::HciHal { return PacketView<kLittleEndian>(shared); } PacketView<kLittleEndian> GetSentCommand() { size_t GetNumSentCommands() { return outgoing_commands_.size(); } CommandPacketView GetSentCommand() { while (outgoing_commands_.size() == 0) ; auto packetview = GetPacketView(std::move(outgoing_commands_.front())); outgoing_commands_.pop_front(); return packetview; return CommandPacketView::Create(packetview); } PacketView<kLittleEndian> GetSentAcl() { Loading Loading @@ -111,10 +115,16 @@ class DependsOnHci : public Module { public: DependsOnHci() : Module() {} void SendHciCommand(std::unique_ptr<CommandPacketBuilder> command) { hci_->EnqueueCommand( std::move(command), common::Bind(&DependsOnHci::handle_event<CommandStatusView>, common::Unretained(this)), common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)), GetHandler()); void SendHciCommandExpectingStatus(std::unique_ptr<CommandPacketBuilder> command) { hci_->EnqueueCommand(std::move(command), common::Bind(&DependsOnHci::handle_event<CommandStatusView>, common::Unretained(this)), GetHandler()); } void SendHciCommandExpectingComplete(std::unique_ptr<CommandPacketBuilder> command) { hci_->EnqueueCommand(std::move(command), common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)), GetHandler()); } void SendAclData(std::unique_ptr<AclPacketBuilder> acl) { Loading Loading @@ -192,6 +202,23 @@ class HciTest : public ::testing::Test { hci = static_cast<HciLayer*>(fake_registry_.GetModuleUnderTest(&HciLayer::Factory)); upper = static_cast<DependsOnHci*>(fake_registry_.GetModuleUnderTest(&DependsOnHci::Factory)); ASSERT(fake_registry_.IsStarted<HciLayer>()); // Wait for the reset while (hal->GetNumSentCommands() == 0) ; // Verify that reset was received ASSERT_EQ(1, hal->GetNumSentCommands()); auto sent_command = hal->GetSentCommand(); auto reset_view = ResetView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(reset_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event uint8_t num_packets = 1; ErrorCode error_code = ErrorCode::SUCCESS; hal->callbacks->hciEventReceived(GetPacketBytes(ResetCompleteBuilder::Create(num_packets, error_code))); } void TearDown() override { Loading @@ -214,6 +241,118 @@ class HciTest : public ::testing::Test { TEST_F(HciTest, initAndClose) {} TEST_F(HciTest, noOpCredits) { ASSERT_EQ(0, hal->GetNumSentCommands()); // Send 0 credits uint8_t num_packets = 0; hal->callbacks->hciEventReceived(GetPacketBytes(NoCommandCompleteBuilder::Create(num_packets))); upper->SendHciCommandExpectingComplete(ReadLocalVersionInformationBuilder::Create()); // Verify that nothing was sent ASSERT_EQ(0, hal->GetNumSentCommands()); num_packets = 1; hal->callbacks->hciEventReceived(GetPacketBytes(NoCommandCompleteBuilder::Create(num_packets))); // Verify that one was sent while (hal->GetNumSentCommands() == 0) ; ASSERT_EQ(1, hal->GetNumSentCommands()); // Send the response event ErrorCode error_code = ErrorCode::SUCCESS; HciVersion hci_version = HciVersion::V_5_0; uint16_t hci_subversion = 0x1234; LmpVersion lmp_version = LmpVersion::V_4_2; uint16_t manufacturer_name = 0xBAD; uint16_t lmp_subversion = 0x5678; hal->callbacks->hciEventReceived(GetPacketBytes(ReadLocalVersionInformationCompleteBuilder::Create( num_packets, error_code, hci_version, hci_subversion, lmp_version, manufacturer_name, lmp_subversion))); auto event = upper->GetReceivedEvent(); ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); } TEST_F(HciTest, creditsTest) { ASSERT_EQ(0, hal->GetNumSentCommands()); // Send all three commands upper->SendHciCommandExpectingComplete(ReadLocalVersionInformationBuilder::Create()); upper->SendHciCommandExpectingComplete(ReadLocalSupportedCommandsBuilder::Create()); upper->SendHciCommandExpectingComplete(ReadLocalSupportedFeaturesBuilder::Create()); while (hal->GetNumSentCommands() == 0) ; // Verify that the first one is sent ASSERT_EQ(1, hal->GetNumSentCommands()); auto sent_command = hal->GetSentCommand(); auto version_view = ReadLocalVersionInformationView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(version_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event uint8_t num_packets = 1; ErrorCode error_code = ErrorCode::SUCCESS; HciVersion hci_version = HciVersion::V_5_0; uint16_t hci_subversion = 0x1234; LmpVersion lmp_version = LmpVersion::V_4_2; uint16_t manufacturer_name = 0xBAD; uint16_t lmp_subversion = 0x5678; hal->callbacks->hciEventReceived(GetPacketBytes(ReadLocalVersionInformationCompleteBuilder::Create( num_packets, error_code, hci_version, hci_subversion, lmp_version, manufacturer_name, lmp_subversion))); auto event = upper->GetReceivedEvent(); ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); // Verify that the second one is sent while (hal->GetNumSentCommands() == 0) ; ASSERT_EQ(1, hal->GetNumSentCommands()); sent_command = hal->GetSentCommand(); auto supported_commands_view = ReadLocalSupportedCommandsView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(supported_commands_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event std::vector<uint8_t> supported_commands; for (uint8_t i = 0; i < 64; i++) { supported_commands.push_back(i); } hal->callbacks->hciEventReceived( GetPacketBytes(ReadLocalSupportedCommandsCompleteBuilder::Create(num_packets, error_code, supported_commands))); event = upper->GetReceivedEvent(); ASSERT(ReadLocalSupportedCommandsCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); // Verify that the third one is sent while (hal->GetNumSentCommands() == 0) ; ASSERT_EQ(1, hal->GetNumSentCommands()); sent_command = hal->GetSentCommand(); auto supported_features_view = ReadLocalSupportedFeaturesView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(supported_features_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event uint64_t lmp_features = 0x012345678abcdef; hal->callbacks->hciEventReceived( GetPacketBytes(ReadLocalSupportedFeaturesCompleteBuilder::Create(num_packets, error_code, lmp_features))); event = upper->GetReceivedEvent(); ASSERT(ReadLocalSupportedFeaturesCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); } TEST_F(HciTest, createConnectionTest) { // Send CreateConnection to the controller common::Address bd_addr; Loading @@ -223,8 +362,8 @@ TEST_F(HciTest, createConnectionTest) { uint16_t clock_offset = 0x3456; ClockOffsetValid clock_offset_valid = ClockOffsetValid::VALID; CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH; upper->SendHciCommand(CreateConnectionBuilder::Create(bd_addr, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch)); upper->SendHciCommandExpectingStatus(CreateConnectionBuilder::Create( bd_addr, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch)); // Check the command auto sent_command = hal->GetSentCommand(); Loading @@ -239,7 +378,7 @@ TEST_F(HciTest, createConnectionTest) { ASSERT_EQ(clock_offset_valid, view.GetClockOffsetValid()); ASSERT_EQ(allow_role_switch, view.GetAllowRoleSwitch()); // Send a ConnectionComplete to the host // Send a Command Status to the host ErrorCode status = ErrorCode::SUCCESS; uint16_t handle = 0x123; LinkType link_type = LinkType::ACL; Loading @@ -251,6 +390,7 @@ TEST_F(HciTest, createConnectionTest) { ASSERT_TRUE(event.IsValid()); ASSERT_EQ(EventCode::COMMAND_STATUS, event.GetEventCode()); // Send a ConnectionComplete to the host hal->callbacks->hciEventReceived( GetPacketBytes(ConnectionCompleteBuilder::Create(status, handle, bd_addr, link_type, encryption_enabled))); Loading system/gd/hci/hci_packets.pdl +61 −1 Original line number Diff line number Diff line Loading @@ -473,6 +473,10 @@ packet CommandStatus : EventPacket (event_code = COMMAND_STATUS){ _payload_, } // Credits packet NoCommandComplete : CommandComplete (command_op_code = NONE){ } // LINK_CONTROL packet Inquiry : DiscoveryCommand (op_code = INQUIRY) { lap0 : 8, // 0x00 - 0x3F Loading Loading @@ -1434,12 +1438,59 @@ packet WriteSecureConnectionsHostSupportComplete : CommandComplete (command_op_c packet ReadLocalVersionInformation : CommandPacket (op_code = READ_LOCAL_VERSION_INFORMATION) { } enum HciVersion : 8 { V_1_0b = 0x00, V_1_1 = 0x01, V_1_2 = 0x02, V_2_0 = 0x03, // + EDR V_2_1 = 0x04, // + EDR V_3_0 = 0x05, // + HS V_4_0 = 0x06, V_4_1 = 0x07, V_4_2 = 0x08, V_5_0 = 0x09, V_5_1 = 0x0a, } enum LmpVersion : 8 { V_1_0b = 0x00, // withdrawn V_1_1 = 0x01, // withdrawn V_1_2 = 0x02, // withdrawn V_2_0 = 0x03, // + EDR V_2_1 = 0x04, // + EDR V_3_0 = 0x05, // + HS V_4_0 = 0x06, V_4_1 = 0x07, V_4_2 = 0x08, V_5_0 = 0x09, V_5_1 = 0x0a, } packet ReadLocalVersionInformationComplete : CommandComplete (command_op_code = READ_LOCAL_VERSION_INFORMATION) { status : ErrorCode, hci_version : HciVersion, hci_revision : 16, lmp_version : LmpVersion, manufacturer_name : 16, lmp_subversion : 16, } packet ReadLocalSupportedCommands : CommandPacket (op_code = READ_LOCAL_SUPPORTED_COMMANDS) { } packet ReadLocalSupportedCommandsComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_COMMANDS) { status : ErrorCode, supported_commands : 8[64], } packet ReadLocalSupportedFeatures : CommandPacket (op_code = READ_LOCAL_SUPPORTED_FEATURES) { } packet ReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_FEATURES) { status : ErrorCode, lmp_features : 64, } packet ReadLocalExtendedFeatures : CommandPacket (op_code = READ_LOCAL_EXTENDED_FEATURES) { page_number : 8, } Loading @@ -1447,6 +1498,14 @@ packet ReadLocalExtendedFeatures : CommandPacket (op_code = READ_LOCAL_EXTENDED_ packet ReadBufferSize : CommandPacket (op_code = READ_BUFFER_SIZE) { } packet ReadBufferSizeComplete : CommandComplete (command_op_code = READ_BUFFER_SIZE) { status : ErrorCode, acl_data_packet_length : 16, synchronous_data_packet_length : 8, total_num_acl_data_packets : 16, total_num_synchronous_data_packets : 16, } packet ReadBdAddr : CommandPacket (op_code = READ_BD_ADDR) { } Loading Loading @@ -2200,7 +2259,8 @@ packet RoleChange : EventPacket (event_code = ROLE_CHANGE){ } packet NumberOfCompletedPackets : EventPacket (event_code = NUMBER_OF_COMPLETED_PACKETS){ _payload_, _count_(handles_and_completed_packets) : 8, handles_and_completed_packets : 32[], // connection_handle[i] : 12, _reserved_[i] : 4, hc_num_of_completed_packets[i] : 16 } enum Mode : 8 { Loading Loading
system/gd/hci/hci_layer.cc +117 −34 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ #include "hci/hci_layer.h" #include "os/alarm.h" #include "common/bind.h" #include "common/callback.h" Loading Loading @@ -45,12 +46,17 @@ class EventHandler { class CommandQueueEntry { public: CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet, OnceCallback<void(CommandStatusView)> on_status_function, OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler) : command(std::move(command_packet)), on_status(std::move(on_status_function)), on_complete(std::move(on_complete_function)), caller_handler(handler) {} : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)), caller_handler(handler) {} CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet, OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler) : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)), caller_handler(handler) {} std::unique_ptr<CommandPacketBuilder> command; bool waiting_for_status_; OnceCallback<void(CommandStatusView)> on_status; OnceCallback<void(CommandCompleteView)> on_complete; Handler* caller_handler; Loading @@ -63,13 +69,32 @@ namespace hci { using common::Address; using common::BidiQueue; using common::BidiQueueEnd; using os::Alarm; using os::Handler; namespace { using hci::OpCode; using hci::ResetCompleteView; void fail_if_reset_complete_not_success(CommandCompleteView complete) { auto reset_complete = ResetCompleteView::Create(complete); ASSERT(reset_complete.IsValid()); ASSERT(reset_complete.GetStatus() == ErrorCode::SUCCESS); } void on_hci_timeout(OpCode op_code) { ASSERT_LOG(false, "Timed out waiting for 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str()); } } // namespace struct HciLayer::impl : public hal::HciHalCallbacks { impl(HciLayer& module) : hal_(nullptr), module_(module) {} ~impl() {} void Start(hal::HciHal* hal) { hal_ = hal; hci_timeout_alarm_ = new Alarm(module_.GetHandler()); auto queue_end = acl_queue_.GetDownEnd(); Handler* handler = module_.GetHandler(); Loading @@ -78,6 +103,7 @@ struct HciLayer::impl : public hal::HciHalCallbacks { handler); RegisterEventHandler(EventCode::COMMAND_STATUS, Bind(&impl::command_status_callback, common::Unretained(this)), handler); EnqueueCommand(ResetBuilder::Create(), BindOnce(&fail_if_reset_complete_not_success), handler); hal_->registerIncomingPacketCallback(this); } Loading @@ -86,6 +112,13 @@ struct HciLayer::impl : public hal::HciHalCallbacks { send_acl(std::move(packet)); } void Stop() { acl_queue_.GetDownEnd()->UnregisterDequeue(); delete hci_timeout_alarm_; command_queue_.clear(); hal_ = nullptr; } void send_acl(std::unique_ptr<hci::BasePacketBuilder> packet) { std::vector<uint8_t> bytes; BitInserter bi(bytes); Loading @@ -100,37 +133,52 @@ struct HciLayer::impl : public hal::HciHalCallbacks { hal_->sendScoData(bytes); } void Stop() { acl_queue_.GetDownEnd()->UnregisterDequeue(); hal_ = nullptr; } void command_status_callback(EventPacketView event) { CommandStatusView status_view = CommandStatusView::Create(event); ASSERT(status_view.IsValid()); if (command_queue_.size() == 0) { ASSERT_LOG(status_view.GetCommandOpCode() == OpCode::NONE, "Unexpected status event with OpCode 0x%02hx", status_view.GetCommandOpCode()); command_credits_ = status_view.GetNumHciCommandPackets(); OpCode op_code = status_view.GetCommandOpCode(); if (op_code == OpCode::NONE) { send_next_command(); return; } // TODO: Check whether this is the CommandOpCode we're looking for. ASSERT_LOG(!command_queue_.empty(), "Unexpected status event with OpCode 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(command_queue_.front().waiting_for_status_, "Waiting for command complete 0x%02hx (%s), got command status for 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); auto caller_handler = command_queue_.front().caller_handler; caller_handler->Post(BindOnce(std::move(command_queue_.front().on_status), std::move(status_view))); command_queue_.pop(); command_queue_.pop_front(); waiting_command_ = OpCode::NONE; hci_timeout_alarm_->Cancel(); send_next_command(); } void command_complete_callback(EventPacketView event) { CommandCompleteView complete_view = CommandCompleteView::Create(event); ASSERT(complete_view.IsValid()); if (command_queue_.size() == 0) { ASSERT_LOG(complete_view.GetCommandOpCode() == OpCode::NONE, "Unexpected command complete event with OpCode 0x%02hx", complete_view.GetCommandOpCode()); command_credits_ = complete_view.GetNumHciCommandPackets(); OpCode op_code = complete_view.GetCommandOpCode(); if (op_code == OpCode::NONE) { send_next_command(); return; } // TODO: Check whether this is the CommandOpCode we're looking for. ASSERT_LOG(command_queue_.size() > 0, "Unexpected command complete with OpCode 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); ASSERT_LOG(!command_queue_.front().waiting_for_status_, "Waiting for command status 0x%02hx (%s), got command complete for 0x%02hx (%s)", waiting_command_, OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); auto caller_handler = command_queue_.front().caller_handler; caller_handler->Post(BindOnce(std::move(command_queue_.front().on_complete), std::move(complete_view))); command_queue_.pop(); command_queue_.pop_front(); waiting_command_ = OpCode::NONE; hci_timeout_alarm_->Cancel(); send_next_command(); } void hciEventReceived(hal::HciPacket event_bytes) override { Loading @@ -147,7 +195,6 @@ struct HciLayer::impl : public hal::HciHalCallbacks { event.GetEventCode()); auto& registered_handler = event_handlers_[event_code].event_handler; event_handlers_[event_code].handler->Post(BindOnce(registered_handler, std::move(event))); // TODO: Credits } void aclDataReceived(hal::HciPacket data_bytes) override { Loading @@ -174,24 +221,53 @@ struct HciLayer::impl : public hal::HciHalCallbacks { ScoPacketView sco = ScoPacketView::Create(packet); } void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command, common::Unretained(this), std::move(command), std::move(on_status), std::move(on_complete), module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_complete, common::Unretained(this), std::move(command), std::move(on_complete), common::Unretained(handler))); } void handle_enqueue_command(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_status, common::Unretained(this), std::move(command), std::move(on_status), common::Unretained(handler))); } void handle_enqueue_command_with_complete(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { command_queue_.emplace(std::move(command), std::move(on_status), std::move(on_complete), handler); command_queue_.emplace_back(std::move(command), std::move(on_complete), handler); if (command_queue_.size() == 1) { std::vector<uint8_t> bytes; BitInserter bi(bytes); command_queue_.front().command->Serialize(bi); hal_->sendHciCommand(bytes); send_next_command(); } void handle_enqueue_command_with_status(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { command_queue_.emplace_back(std::move(command), std::move(on_status), handler); send_next_command(); } void send_next_command() { if (command_credits_ == 0) { return; } if (waiting_command_ != OpCode::NONE) { return; } if (command_queue_.size() == 0) { return; } std::shared_ptr<std::vector<uint8_t>> bytes = std::make_shared<std::vector<uint8_t>>(); BitInserter bi(*bytes); command_queue_.front().command->Serialize(bi); hal_->sendHciCommand(*bytes); auto cmd_view = CommandPacketView::Create(bytes); ASSERT(cmd_view.IsValid()); OpCode op_code = cmd_view.GetOpCode(); waiting_command_ = op_code; command_credits_ = 0; // Only allow one outstanding command hci_timeout_alarm_->Schedule(BindOnce(&on_hci_timeout, op_code), kHciTimeoutMs); } BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd() { Loading Loading @@ -227,9 +303,12 @@ struct HciLayer::impl : public hal::HciHalCallbacks { HciLayer& module_; // Command Handling std::queue<CommandQueueEntry> command_queue_; std::list<CommandQueueEntry> command_queue_; std::map<EventCode, EventHandler> event_handlers_; OpCode waiting_command_{OpCode::NONE}; uint8_t command_credits_{1}; // Send reset first Alarm* hci_timeout_alarm_{nullptr}; // Acl packets BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */}; Loading @@ -242,9 +321,13 @@ HciLayer::~HciLayer() { } void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { impl_->EnqueueCommand(std::move(command), std::move(on_status), std::move(on_complete), handler); impl_->EnqueueCommand(std::move(command), std::move(on_complete), handler); } void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { impl_->EnqueueCommand(std::move(command), std::move(on_status), handler); } common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* HciLayer::GetAclQueueEnd() { Loading
system/gd/hci/hci_layer.h +5 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <chrono> #include <map> #include "common/address.h" Loading @@ -37,9 +38,11 @@ class HciLayer : public Module { DISALLOW_COPY_AND_ASSIGN(HciLayer); virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler); virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler); virtual common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd(); virtual void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler, Loading @@ -54,6 +57,7 @@ class HciLayer : public Module { void Start() override; void Stop() override; static constexpr std::chrono::milliseconds kHciTimeoutMs = std::chrono::milliseconds(2000); private: struct impl; Loading
system/gd/hci/hci_layer_test.cc +149 −9 Original line number Diff line number Diff line Loading @@ -75,12 +75,16 @@ class TestHciHal : public hal::HciHal { return PacketView<kLittleEndian>(shared); } PacketView<kLittleEndian> GetSentCommand() { size_t GetNumSentCommands() { return outgoing_commands_.size(); } CommandPacketView GetSentCommand() { while (outgoing_commands_.size() == 0) ; auto packetview = GetPacketView(std::move(outgoing_commands_.front())); outgoing_commands_.pop_front(); return packetview; return CommandPacketView::Create(packetview); } PacketView<kLittleEndian> GetSentAcl() { Loading Loading @@ -111,10 +115,16 @@ class DependsOnHci : public Module { public: DependsOnHci() : Module() {} void SendHciCommand(std::unique_ptr<CommandPacketBuilder> command) { hci_->EnqueueCommand( std::move(command), common::Bind(&DependsOnHci::handle_event<CommandStatusView>, common::Unretained(this)), common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)), GetHandler()); void SendHciCommandExpectingStatus(std::unique_ptr<CommandPacketBuilder> command) { hci_->EnqueueCommand(std::move(command), common::Bind(&DependsOnHci::handle_event<CommandStatusView>, common::Unretained(this)), GetHandler()); } void SendHciCommandExpectingComplete(std::unique_ptr<CommandPacketBuilder> command) { hci_->EnqueueCommand(std::move(command), common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)), GetHandler()); } void SendAclData(std::unique_ptr<AclPacketBuilder> acl) { Loading Loading @@ -192,6 +202,23 @@ class HciTest : public ::testing::Test { hci = static_cast<HciLayer*>(fake_registry_.GetModuleUnderTest(&HciLayer::Factory)); upper = static_cast<DependsOnHci*>(fake_registry_.GetModuleUnderTest(&DependsOnHci::Factory)); ASSERT(fake_registry_.IsStarted<HciLayer>()); // Wait for the reset while (hal->GetNumSentCommands() == 0) ; // Verify that reset was received ASSERT_EQ(1, hal->GetNumSentCommands()); auto sent_command = hal->GetSentCommand(); auto reset_view = ResetView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(reset_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event uint8_t num_packets = 1; ErrorCode error_code = ErrorCode::SUCCESS; hal->callbacks->hciEventReceived(GetPacketBytes(ResetCompleteBuilder::Create(num_packets, error_code))); } void TearDown() override { Loading @@ -214,6 +241,118 @@ class HciTest : public ::testing::Test { TEST_F(HciTest, initAndClose) {} TEST_F(HciTest, noOpCredits) { ASSERT_EQ(0, hal->GetNumSentCommands()); // Send 0 credits uint8_t num_packets = 0; hal->callbacks->hciEventReceived(GetPacketBytes(NoCommandCompleteBuilder::Create(num_packets))); upper->SendHciCommandExpectingComplete(ReadLocalVersionInformationBuilder::Create()); // Verify that nothing was sent ASSERT_EQ(0, hal->GetNumSentCommands()); num_packets = 1; hal->callbacks->hciEventReceived(GetPacketBytes(NoCommandCompleteBuilder::Create(num_packets))); // Verify that one was sent while (hal->GetNumSentCommands() == 0) ; ASSERT_EQ(1, hal->GetNumSentCommands()); // Send the response event ErrorCode error_code = ErrorCode::SUCCESS; HciVersion hci_version = HciVersion::V_5_0; uint16_t hci_subversion = 0x1234; LmpVersion lmp_version = LmpVersion::V_4_2; uint16_t manufacturer_name = 0xBAD; uint16_t lmp_subversion = 0x5678; hal->callbacks->hciEventReceived(GetPacketBytes(ReadLocalVersionInformationCompleteBuilder::Create( num_packets, error_code, hci_version, hci_subversion, lmp_version, manufacturer_name, lmp_subversion))); auto event = upper->GetReceivedEvent(); ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); } TEST_F(HciTest, creditsTest) { ASSERT_EQ(0, hal->GetNumSentCommands()); // Send all three commands upper->SendHciCommandExpectingComplete(ReadLocalVersionInformationBuilder::Create()); upper->SendHciCommandExpectingComplete(ReadLocalSupportedCommandsBuilder::Create()); upper->SendHciCommandExpectingComplete(ReadLocalSupportedFeaturesBuilder::Create()); while (hal->GetNumSentCommands() == 0) ; // Verify that the first one is sent ASSERT_EQ(1, hal->GetNumSentCommands()); auto sent_command = hal->GetSentCommand(); auto version_view = ReadLocalVersionInformationView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(version_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event uint8_t num_packets = 1; ErrorCode error_code = ErrorCode::SUCCESS; HciVersion hci_version = HciVersion::V_5_0; uint16_t hci_subversion = 0x1234; LmpVersion lmp_version = LmpVersion::V_4_2; uint16_t manufacturer_name = 0xBAD; uint16_t lmp_subversion = 0x5678; hal->callbacks->hciEventReceived(GetPacketBytes(ReadLocalVersionInformationCompleteBuilder::Create( num_packets, error_code, hci_version, hci_subversion, lmp_version, manufacturer_name, lmp_subversion))); auto event = upper->GetReceivedEvent(); ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); // Verify that the second one is sent while (hal->GetNumSentCommands() == 0) ; ASSERT_EQ(1, hal->GetNumSentCommands()); sent_command = hal->GetSentCommand(); auto supported_commands_view = ReadLocalSupportedCommandsView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(supported_commands_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event std::vector<uint8_t> supported_commands; for (uint8_t i = 0; i < 64; i++) { supported_commands.push_back(i); } hal->callbacks->hciEventReceived( GetPacketBytes(ReadLocalSupportedCommandsCompleteBuilder::Create(num_packets, error_code, supported_commands))); event = upper->GetReceivedEvent(); ASSERT(ReadLocalSupportedCommandsCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); // Verify that the third one is sent while (hal->GetNumSentCommands() == 0) ; ASSERT_EQ(1, hal->GetNumSentCommands()); sent_command = hal->GetSentCommand(); auto supported_features_view = ReadLocalSupportedFeaturesView::Create(CommandPacketView::Create(sent_command)); ASSERT_TRUE(supported_features_view.IsValid()); // Verify that only one was sent ASSERT_EQ(0, hal->GetNumSentCommands()); // Send the response event uint64_t lmp_features = 0x012345678abcdef; hal->callbacks->hciEventReceived( GetPacketBytes(ReadLocalSupportedFeaturesCompleteBuilder::Create(num_packets, error_code, lmp_features))); event = upper->GetReceivedEvent(); ASSERT(ReadLocalSupportedFeaturesCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event))) .IsValid()); } TEST_F(HciTest, createConnectionTest) { // Send CreateConnection to the controller common::Address bd_addr; Loading @@ -223,8 +362,8 @@ TEST_F(HciTest, createConnectionTest) { uint16_t clock_offset = 0x3456; ClockOffsetValid clock_offset_valid = ClockOffsetValid::VALID; CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH; upper->SendHciCommand(CreateConnectionBuilder::Create(bd_addr, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch)); upper->SendHciCommandExpectingStatus(CreateConnectionBuilder::Create( bd_addr, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch)); // Check the command auto sent_command = hal->GetSentCommand(); Loading @@ -239,7 +378,7 @@ TEST_F(HciTest, createConnectionTest) { ASSERT_EQ(clock_offset_valid, view.GetClockOffsetValid()); ASSERT_EQ(allow_role_switch, view.GetAllowRoleSwitch()); // Send a ConnectionComplete to the host // Send a Command Status to the host ErrorCode status = ErrorCode::SUCCESS; uint16_t handle = 0x123; LinkType link_type = LinkType::ACL; Loading @@ -251,6 +390,7 @@ TEST_F(HciTest, createConnectionTest) { ASSERT_TRUE(event.IsValid()); ASSERT_EQ(EventCode::COMMAND_STATUS, event.GetEventCode()); // Send a ConnectionComplete to the host hal->callbacks->hciEventReceived( GetPacketBytes(ConnectionCompleteBuilder::Create(status, handle, bd_addr, link_type, encryption_enabled))); Loading
system/gd/hci/hci_packets.pdl +61 −1 Original line number Diff line number Diff line Loading @@ -473,6 +473,10 @@ packet CommandStatus : EventPacket (event_code = COMMAND_STATUS){ _payload_, } // Credits packet NoCommandComplete : CommandComplete (command_op_code = NONE){ } // LINK_CONTROL packet Inquiry : DiscoveryCommand (op_code = INQUIRY) { lap0 : 8, // 0x00 - 0x3F Loading Loading @@ -1434,12 +1438,59 @@ packet WriteSecureConnectionsHostSupportComplete : CommandComplete (command_op_c packet ReadLocalVersionInformation : CommandPacket (op_code = READ_LOCAL_VERSION_INFORMATION) { } enum HciVersion : 8 { V_1_0b = 0x00, V_1_1 = 0x01, V_1_2 = 0x02, V_2_0 = 0x03, // + EDR V_2_1 = 0x04, // + EDR V_3_0 = 0x05, // + HS V_4_0 = 0x06, V_4_1 = 0x07, V_4_2 = 0x08, V_5_0 = 0x09, V_5_1 = 0x0a, } enum LmpVersion : 8 { V_1_0b = 0x00, // withdrawn V_1_1 = 0x01, // withdrawn V_1_2 = 0x02, // withdrawn V_2_0 = 0x03, // + EDR V_2_1 = 0x04, // + EDR V_3_0 = 0x05, // + HS V_4_0 = 0x06, V_4_1 = 0x07, V_4_2 = 0x08, V_5_0 = 0x09, V_5_1 = 0x0a, } packet ReadLocalVersionInformationComplete : CommandComplete (command_op_code = READ_LOCAL_VERSION_INFORMATION) { status : ErrorCode, hci_version : HciVersion, hci_revision : 16, lmp_version : LmpVersion, manufacturer_name : 16, lmp_subversion : 16, } packet ReadLocalSupportedCommands : CommandPacket (op_code = READ_LOCAL_SUPPORTED_COMMANDS) { } packet ReadLocalSupportedCommandsComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_COMMANDS) { status : ErrorCode, supported_commands : 8[64], } packet ReadLocalSupportedFeatures : CommandPacket (op_code = READ_LOCAL_SUPPORTED_FEATURES) { } packet ReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_FEATURES) { status : ErrorCode, lmp_features : 64, } packet ReadLocalExtendedFeatures : CommandPacket (op_code = READ_LOCAL_EXTENDED_FEATURES) { page_number : 8, } Loading @@ -1447,6 +1498,14 @@ packet ReadLocalExtendedFeatures : CommandPacket (op_code = READ_LOCAL_EXTENDED_ packet ReadBufferSize : CommandPacket (op_code = READ_BUFFER_SIZE) { } packet ReadBufferSizeComplete : CommandComplete (command_op_code = READ_BUFFER_SIZE) { status : ErrorCode, acl_data_packet_length : 16, synchronous_data_packet_length : 8, total_num_acl_data_packets : 16, total_num_synchronous_data_packets : 16, } packet ReadBdAddr : CommandPacket (op_code = READ_BD_ADDR) { } Loading Loading @@ -2200,7 +2259,8 @@ packet RoleChange : EventPacket (event_code = ROLE_CHANGE){ } packet NumberOfCompletedPackets : EventPacket (event_code = NUMBER_OF_COMPLETED_PACKETS){ _payload_, _count_(handles_and_completed_packets) : 8, handles_and_completed_packets : 32[], // connection_handle[i] : 12, _reserved_[i] : 4, hc_num_of_completed_packets[i] : 16 } enum Mode : 8 { Loading