diff options
| author | 2023-06-07 18:07:49 +0000 | |
|---|---|---|
| committer | 2023-06-14 17:55:44 +0000 | |
| commit | a9635a2a9158b272631c3216fee13abca32d1bdc (patch) | |
| tree | c8e79be2305989985c92c9dc9e4589dc837b3792 | |
| parent | 1f3c9b0e0bc560d88a48508885f922a5fb3608ba (diff) | |
RootCanal: Implement all Role Change configurations
Reland original change Idf251f4e8234c015fc43863cd3d7f7928a510c2d.
Bug: 274248798
Test: atest --host rootcanal_ll_test
Change-Id: I6b3ab4ff9f6636a8fe4ddd9b0e9c71832ebe7783
| -rw-r--r-- | tools/rootcanal/Android.bp | 1 | ||||
| -rw-r--r-- | tools/rootcanal/config.proto | 5 | ||||
| -rw-r--r-- | tools/rootcanal/model/controller/acl_connection.cc | 4 | ||||
| -rw-r--r-- | tools/rootcanal/model/controller/acl_connection.h | 16 | ||||
| -rw-r--r-- | tools/rootcanal/model/controller/controller_properties.cc | 1 | ||||
| -rw-r--r-- | tools/rootcanal/model/controller/link_layer_controller.cc | 215 | ||||
| -rw-r--r-- | tools/rootcanal/packets/link_layer_packets.pdl | 2 | ||||
| -rw-r--r-- | tools/rootcanal/py/bluetooth.py | 11 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_01_C.py | 63 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_02_C.py | 66 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_142_C.py | 53 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_143_C.py | 72 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_144_C.py | 41 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_149_C.py | 68 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_78_C.py | 41 | ||||
| -rw-r--r-- | tools/rootcanal/test/LMP/LIH/BV_79_C.py | 43 | ||||
| -rw-r--r-- | tools/rootcanal/test/main.py | 8 |
17 files changed, 623 insertions, 87 deletions
diff --git a/tools/rootcanal/Android.bp b/tools/rootcanal/Android.bp index c84cc456f1..d50bccdfe7 100644 --- a/tools/rootcanal/Android.bp +++ b/tools/rootcanal/Android.bp @@ -310,6 +310,7 @@ python_test_host { "test/LL/CON_/PER/*.py", "test/LL/DDI/ADV/*.py", "test/LL/DDI/SCN/*.py", + "test/LMP/LIH/*.py", "test/main.py", ], data: [ diff --git a/tools/rootcanal/config.proto b/tools/rootcanal/config.proto index 317ba2e4e3..32b452ddc2 100644 --- a/tools/rootcanal/config.proto +++ b/tools/rootcanal/config.proto @@ -29,11 +29,8 @@ message ControllerQuirks { optional bool send_acl_data_before_connection_complete = 1; // Configure a default value for the LE random address. optional bool has_default_random_address = 2; - // Send the Role Change event before the Connection Complete event - // in the case where a role switch is initiated at connection establishment. - optional bool send_role_change_before_connection_complete = 3; // Send an Hardware Error event if any command is called before HCI Reset. - optional bool hardware_error_before_reset = 4; + optional bool hardware_error_before_reset = 3; } message Controller { diff --git a/tools/rootcanal/model/controller/acl_connection.cc b/tools/rootcanal/model/controller/acl_connection.cc index 2c47ad191d..926e373aa1 100644 --- a/tools/rootcanal/model/controller/acl_connection.cc +++ b/tools/rootcanal/model/controller/acl_connection.cc @@ -33,10 +33,6 @@ void AclConnection::Encrypt() { encrypted_ = true; }; bool AclConnection::IsEncrypted() const { return encrypted_; }; -uint16_t AclConnection::GetLinkPolicySettings() const { - return link_policy_settings_; -}; - void AclConnection::SetLinkPolicySettings(uint16_t settings) { link_policy_settings_ = settings; } diff --git a/tools/rootcanal/model/controller/acl_connection.h b/tools/rootcanal/model/controller/acl_connection.h index 74bc9a700a..7945ef0ece 100644 --- a/tools/rootcanal/model/controller/acl_connection.h +++ b/tools/rootcanal/model/controller/acl_connection.h @@ -26,6 +26,12 @@ namespace rootcanal { using ::bluetooth::hci::AddressWithType; +enum AclConnectionState { + kActiveMode, + kHoldMode, + kSniffMode, +}; + // Model the connection of a device to the controller. class AclConnection { public: @@ -44,8 +50,15 @@ class AclConnection { void Encrypt(); bool IsEncrypted() const; - uint16_t GetLinkPolicySettings() const; void SetLinkPolicySettings(uint16_t settings); + uint16_t GetLinkPolicySettings() const { return link_policy_settings_; } + bool IsRoleSwitchEnabled() const { + return (link_policy_settings_ & 0x1) != 0; + } + bool IsHoldModeEnabled() const { return (link_policy_settings_ & 0x2) != 0; } + bool IsSniffModeEnabled() const { return (link_policy_settings_ & 0x4) != 0; } + + AclConnectionState GetMode() const { return state_; } bluetooth::hci::Role GetRole() const; void SetRole(bluetooth::hci::Role role); @@ -81,6 +94,7 @@ class AclConnection { // State variables bool encrypted_{false}; uint16_t link_policy_settings_{0}; + AclConnectionState state_{kActiveMode}; bluetooth::hci::Role role_{bluetooth::hci::Role::CENTRAL}; std::chrono::steady_clock::time_point last_packet_timestamp_; std::chrono::steady_clock::duration timeout_; diff --git a/tools/rootcanal/model/controller/controller_properties.cc b/tools/rootcanal/model/controller/controller_properties.cc index 94f59a4b5f..8105fd9007 100644 --- a/tools/rootcanal/model/controller/controller_properties.cc +++ b/tools/rootcanal/model/controller/controller_properties.cc @@ -1963,7 +1963,6 @@ ControllerProperties::ControllerProperties( config.quirks().hardware_error_before_reset(); } // TODO(b/270606199): support send_acl_data_before_connection_complete - // TODO(b/274476773): support send_role_change_before_connection_complete } if (!CheckSupportedFeatures()) { diff --git a/tools/rootcanal/model/controller/link_layer_controller.cc b/tools/rootcanal/model/controller/link_layer_controller.cc index 0f84a8a160..b77bbf2e70 100644 --- a/tools/rootcanal/model/controller/link_layer_controller.cc +++ b/tools/rootcanal/model/controller/link_layer_controller.cc @@ -5065,32 +5065,39 @@ void LinkLayerController::IncomingPageResponsePacket( WARNING(id_, "No free handles"); return; } + CancelScheduledTask(page_timeout_task_id_); ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(peer.data()))); CheckExpiringConnection(handle); - auto addr = incoming.GetSourceAddress(); + AclConnection& connection = connections_.GetAclConnection(handle); + auto bd_addr = incoming.GetSourceAddress(); auto response = model::packets::PageResponseView::Create(incoming); ASSERT(response.IsValid()); - /* Role change event before connection complete is a quirk commonly exists in - * Android-capatable Bluetooth controllers. - * On the initiator side, only connection in peripheral role should be - * accompanied with a role change event */ - // TODO(b/274476773): Add a config option for this quirk - if (connections_.IsRoleSwitchAllowedForPendingConnection() && - response.GetTryRoleSwitch()) { - auto role = bluetooth::hci::Role::PERIPHERAL; - connections_.SetAclRole(handle, role); - if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { - send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, - addr, role)); - } + + bluetooth::hci::Role role = + connections_.IsRoleSwitchAllowedForPendingConnection() && + response.GetTryRoleSwitch() + ? bluetooth::hci::Role::PERIPHERAL + : bluetooth::hci::Role::CENTRAL; + + connection.SetLinkPolicySettings(default_link_policy_settings_); + connection.SetRole(role); + + // Role change event before connection complete generates an HCI Role Change + // event on the initiator side if accepted; the event is sent before the + // HCI Connection Complete event. + if (role == bluetooth::hci::Role::PERIPHERAL && + IsEventUnmasked(EventCode::ROLE_CHANGE)) { + send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, + bd_addr, role)); } + if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( - ErrorCode::SUCCESS, handle, addr, bluetooth::hci::LinkType::ACL, + ErrorCode::SUCCESS, handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } } @@ -5223,42 +5230,49 @@ ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& bd_addr, return ErrorCode::UNKNOWN_CONNECTION; } -void LinkLayerController::MakePeripheralConnection(const Address& addr, +void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, bool try_role_switch) { - INFO(id_, "Sending page response to {}", addr); + INFO(id_, "Sending page response to {}", bd_addr); SendLinkLayerPacket(model::packets::PageResponseBuilder::Create( - GetAddress(), addr, try_role_switch)); + GetAddress(), bd_addr, try_role_switch)); - uint16_t handle = connections_.CreateConnection(addr, GetAddress()); - if (handle == kReservedHandle) { + uint16_t connection_handle = + connections_.CreateConnection(bd_addr, GetAddress()); + if (connection_handle == kReservedHandle) { INFO(id_, "CreateConnection failed"); return; } + ASSERT(link_manager_add_link( - lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(addr.data()))); + lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); - CheckExpiringConnection(handle); + CheckExpiringConnection(connection_handle); - /* Role change event before connection complete is a quirk commonly exists in - * Android-capatable Bluetooth controllers. - * On the responder side, any connection should be accompanied with a role - * change event */ - // TODO(b/274476773): Add a config option for this quirk - auto role = + bluetooth::hci::Role role = try_role_switch && connections_.IsRoleSwitchAllowedForPendingConnection() ? bluetooth::hci::Role::CENTRAL : bluetooth::hci::Role::PERIPHERAL; - connections_.SetAclRole(handle, role); - if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { + + AclConnection& connection = connections_.GetAclConnection(connection_handle); + + connection.SetLinkPolicySettings(default_link_policy_settings_); + connection.SetRole(role); + + // Role change event before connection complete generates an HCI Role Change + // event on the acceptor side if accepted; the event is sent before the + // HCI Connection Complete event. + if (role == bluetooth::hci::Role::CENTRAL && + IsEventUnmasked(EventCode::ROLE_CHANGE)) { + INFO(id_, "Role at connection setup accepted"); send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, - addr, role)); + bd_addr, role)); } - INFO(id_, "CreateConnection returned handle 0x{:x}", handle); + INFO(id_, "CreateConnection returned handle 0x{:x}", connection_handle); if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( - ErrorCode::SUCCESS, handle, addr, bluetooth::hci::LinkType::ACL, - bluetooth::hci::Enable::DISABLED)); + ErrorCode::SUCCESS, connection_handle, bd_addr, + bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } } @@ -5488,67 +5502,124 @@ ErrorCode LinkLayerController::RoleDiscovery(uint16_t handle, if (!connections_.HasHandle(handle)) { return ErrorCode::UNKNOWN_CONNECTION; } + *role = connections_.GetAclRole(handle); return ErrorCode::SUCCESS; } -ErrorCode LinkLayerController::SwitchRole(Address addr, +ErrorCode LinkLayerController::SwitchRole(Address bd_addr, bluetooth::hci::Role role) { - auto handle = connections_.GetHandleOnlyAddress(addr); - if (handle == rootcanal::kReservedHandle) { + // The BD_ADDR command parameter indicates for which connection + // the role switch is to be performed and shall specify a BR/EDR Controller + // for which a connection already exists. + uint16_t connection_handle = connections_.GetHandleOnlyAddress(bd_addr); + if (connection_handle == kReservedHandle) { + INFO(id_, "unknown connection address {}", bd_addr); return ErrorCode::UNKNOWN_CONNECTION; } - // TODO(b/274248798): Reject role switch if disabled in link policy - SendLinkLayerPacket(model::packets::RoleSwitchRequestBuilder::Create( - GetAddress(), addr, static_cast<uint8_t>(role))); + + AclConnection& connection = connections_.GetAclConnection(connection_handle); + + // If there is an (e)SCO connection between the local device and the device + // identified by the BD_ADDR parameter, an attempt to perform a role switch + // shall be rejected by the local device. + if (connections_.GetScoHandle(bd_addr) != kReservedHandle) { + INFO(id_, + "role switch rejected because an Sco link is opened with" + " the target device"); + return ErrorCode::COMMAND_DISALLOWED; + } + + // If the connection between the local device and the device identified by the + // BD_ADDR parameter is placed in Sniff mode, an attempt to perform a role + // switch shall be rejected by the local device. + if (connection.GetMode() == AclConnectionState::kSniffMode) { + INFO(id_, + "role switch rejected because the acl connection is in sniff mode"); + return ErrorCode::COMMAND_DISALLOWED; + } + + if (role != connection.GetRole()) { + SendLinkLayerPacket(model::packets::RoleSwitchRequestBuilder::Create( + GetAddress(), bd_addr)); + } else if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { + // Note: the status is Success only if the role change procedure was + // actually performed, otherwise the status is >0. + ScheduleTask(kNoDelayMs, [this, bd_addr, role]() { + send_event_(bluetooth::hci::RoleChangeBuilder::Create( + ErrorCode::ROLE_SWITCH_FAILED, bd_addr, role)); + }); + } + return ErrorCode::SUCCESS; } void LinkLayerController::IncomingRoleSwitchRequest( model::packets::LinkLayerPacketView incoming) { - auto addr = incoming.GetSourceAddress(); - auto handle = connections_.GetHandleOnlyAddress(addr); - auto request = model::packets::RoleSwitchRequestView::Create(incoming); - ASSERT(request.IsValid()); + auto bd_addr = incoming.GetSourceAddress(); + auto connection_handle = connections_.GetHandleOnlyAddress(bd_addr); + auto switch_req = model::packets::RoleSwitchRequestView::Create(incoming); + ASSERT(switch_req.IsValid()); - // TODO(b/274248798): Reject role switch if disabled in link policy - Role remote_role = static_cast<Role>(request.GetInitiatorNewRole()); - Role local_role = - remote_role == Role::CENTRAL ? Role::PERIPHERAL : Role::CENTRAL; - connections_.SetAclRole(handle, local_role); - if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { - ScheduleTask(kNoDelayMs, [this, addr, local_role]() { - send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS, - addr, local_role)); - }); + if (connection_handle == kReservedHandle) { + INFO(id_, "ignoring Switch Request received on unknown connection"); + return; } - ScheduleTask(kNoDelayMs, [this, addr, remote_role]() { + + AclConnection& connection = connections_.GetAclConnection(connection_handle); + + if (!connection.IsRoleSwitchEnabled()) { + INFO(id_, "role switch disabled by local link policy settings"); SendLinkLayerPacket(model::packets::RoleSwitchResponseBuilder::Create( - GetAddress(), addr, static_cast<uint8_t>(ErrorCode::SUCCESS), - static_cast<uint8_t>(remote_role))); - }); + GetAddress(), bd_addr, + static_cast<uint8_t>(ErrorCode::ROLE_CHANGE_NOT_ALLOWED))); + } else { + INFO(id_, "role switch request accepted by local device"); + SendLinkLayerPacket(model::packets::RoleSwitchResponseBuilder::Create( + GetAddress(), bd_addr, static_cast<uint8_t>(ErrorCode::SUCCESS))); + + bluetooth::hci::Role new_role = + connection.GetRole() == bluetooth::hci::Role::CENTRAL + ? bluetooth::hci::Role::PERIPHERAL + : bluetooth::hci::Role::CENTRAL; + + connection.SetRole(new_role); + + if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { + ScheduleTask(kNoDelayMs, [this, bd_addr, new_role]() { + send_event_(bluetooth::hci::RoleChangeBuilder::Create( + ErrorCode::SUCCESS, bd_addr, new_role)); + }); + } + } } void LinkLayerController::IncomingRoleSwitchResponse( model::packets::LinkLayerPacketView incoming) { - auto addr = incoming.GetSourceAddress(); - auto handle = connections_.GetHandleOnlyAddress(addr); - auto response = model::packets::RoleSwitchResponseView::Create(incoming); - ASSERT(response.IsValid()); + auto bd_addr = incoming.GetSourceAddress(); + auto connection_handle = connections_.GetHandleOnlyAddress(bd_addr); + auto switch_rsp = model::packets::RoleSwitchResponseView::Create(incoming); + ASSERT(switch_rsp.IsValid()); - // TODO(b/274248798): Reject role switch if disabled in link policy - ErrorCode status = ErrorCode::SUCCESS; - Role role = static_cast<Role>(response.GetInitiatorNewRole()); - if (response.GetStatus() == static_cast<uint8_t>(ErrorCode::SUCCESS)) { - connections_.SetAclRole(handle, role); - } else { - status = static_cast<ErrorCode>(response.GetStatus()); + if (connection_handle == kReservedHandle) { + INFO(id_, "ignoring Switch Response received on unknown connection"); + return; } + AclConnection& connection = connections_.GetAclConnection(connection_handle); + ErrorCode status = ErrorCode(switch_rsp.GetStatus()); + bluetooth::hci::Role new_role = + status != ErrorCode::SUCCESS ? connection.GetRole() + : connection.GetRole() == bluetooth::hci::Role::CENTRAL + ? bluetooth::hci::Role::PERIPHERAL + : bluetooth::hci::Role::CENTRAL; + + connection.SetRole(new_role); + if (IsEventUnmasked(EventCode::ROLE_CHANGE)) { - ScheduleTask(kNoDelayMs, [this, status, addr, role]() { + ScheduleTask(kNoDelayMs, [this, status, bd_addr, new_role]() { send_event_( - bluetooth::hci::RoleChangeBuilder::Create(status, addr, role)); + bluetooth::hci::RoleChangeBuilder::Create(status, bd_addr, new_role)); }); } } @@ -5558,6 +5629,7 @@ ErrorCode LinkLayerController::ReadLinkPolicySettings(uint16_t handle, if (!connections_.HasHandle(handle)) { return ErrorCode::UNKNOWN_CONNECTION; } + *settings = connections_.GetAclLinkPolicySettings(handle); return ErrorCode::SUCCESS; } @@ -5579,6 +5651,7 @@ ErrorCode LinkLayerController::WriteDefaultLinkPolicySettings( if (settings > 7 /* Sniff + Hold + Role switch */) { return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS; } + default_link_policy_settings_ = settings; return ErrorCode::SUCCESS; } diff --git a/tools/rootcanal/packets/link_layer_packets.pdl b/tools/rootcanal/packets/link_layer_packets.pdl index 131ebc3c22..39b4ed2f5e 100644 --- a/tools/rootcanal/packets/link_layer_packets.pdl +++ b/tools/rootcanal/packets/link_layer_packets.pdl @@ -356,12 +356,10 @@ packet PingResponse : LinkLayerPacket (type = PING_RESPONSE) { } packet RoleSwitchRequest : LinkLayerPacket (type = ROLE_SWITCH_REQUEST) { - initiator_new_role: 8, } packet RoleSwitchResponse : LinkLayerPacket (type = ROLE_SWITCH_RESPONSE) { status: 8, - initiator_new_role: 8, } packet LlPhyReq : LinkLayerPacket (type = LL_PHY_REQ) { diff --git a/tools/rootcanal/py/bluetooth.py b/tools/rootcanal/py/bluetooth.py index 0244333722..a8a18c53ae 100644 --- a/tools/rootcanal/py/bluetooth.py +++ b/tools/rootcanal/py/bluetooth.py @@ -51,16 +51,19 @@ class Address: @dataclass class ClassOfDevice: + class_of_device: int = 0 def parse(span: bytes) -> Tuple['Address', bytes]: - assert False + assert len(span) >= 3 + return (ClassOfDevice(int.from_bytes(span[:3], byteorder='little')), span[3:]) def parse_all(span: bytes) -> 'Address': - assert False + assert len(span) == 3 + return ClassOfDevice(int.from_bytes(span, byteorder='little')) def serialize(self) -> bytes: - assert False + return int.to_bytes(self.class_of_device, length=3, byteorder='little') @property def size(self) -> int: - assert False + return 3 diff --git a/tools/rootcanal/test/LMP/LIH/BV_01_C.py b/tools/rootcanal/test/LMP/LIH/BV_01_C.py new file mode 100644 index 0000000000..ca92e44e30 --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_01_C.py @@ -0,0 +1,63 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-01-C [Initiate Role Switch] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd(hci.WriteScanEnable(scan_enable=hci.ScanEnable.PAGE_SCAN_ONLY)) + + await self.expect_evt(hci.WriteScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + controller.send_ll( + ll.Page(source_address=peer_address, destination_address=controller.address, allow_role_switch=False)) + + await self.expect_evt(hci.ConnectionRequest(bd_addr=peer_address, link_type=hci.ConnectionRequestLinkType.ACL)) + + controller.send_cmd( + hci.AcceptConnectionRequest(bd_addr=peer_address, role=hci.AcceptConnectionRequestRole.REMAIN_PERIPHERAL)) + + await self.expect_evt(hci.AcceptConnectionRequestStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.PageResponse(source_address=controller.address, destination_address=peer_address, try_role_switch=False)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) + + controller.send_cmd( + hci.WriteLinkPolicySettings(connection_handle=acl_connection_handle, + link_policy_settings=hci.LinkPolicy.ENABLE_ROLE_SWITCH)) + + await self.expect_evt( + hci.WriteLinkPolicySettingsComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + connection_handle=acl_connection_handle)) + + controller.send_cmd(hci.SwitchRole(bd_addr=peer_address, role=hci.Role.CENTRAL)) + + await self.expect_evt(hci.SwitchRoleStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll(ll.RoleSwitchRequest(source_address=controller.address, destination_address=peer_address)) + + controller.send_ll( + ll.RoleSwitchResponse(source_address=peer_address, + destination_address=controller.address, + status=ErrorCode.SUCCESS)) + + await self.expect_evt(hci.RoleChange(status=ErrorCode.SUCCESS, bd_addr=peer_address, new_role=hci.Role.CENTRAL)) diff --git a/tools/rootcanal/test/LMP/LIH/BV_02_C.py b/tools/rootcanal/test/LMP/LIH/BV_02_C.py new file mode 100644 index 0000000000..375acdc1f6 --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_02_C.py @@ -0,0 +1,66 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-02-C [Accept Role Switch] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd( + hci.CreateConnection(bd_addr=peer_address, + packet_type=0x7fff, + page_scan_repetition_mode=hci.PageScanRepetitionMode.R0, + allow_role_switch=hci.CreateConnectionRoleSwitch.REMAIN_CENTRAL)) + + await self.expect_evt(hci.CreateConnectionStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.Page(source_address=controller.address, destination_address=peer_address, allow_role_switch=False)) + + controller.send_ll( + ll.PageResponse(source_address=peer_address, destination_address=controller.address, try_role_switch=False)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) + + controller.send_cmd( + hci.WriteLinkPolicySettings(connection_handle=acl_connection_handle, + link_policy_settings=hci.LinkPolicy.ENABLE_ROLE_SWITCH)) + + await self.expect_evt( + hci.WriteLinkPolicySettingsComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + connection_handle=acl_connection_handle)) + + controller.send_ll(ll.RoleSwitchRequest(source_address=peer_address, destination_address=controller.address)) + + await self.expect_ll( + ll.RoleSwitchResponse(source_address=controller.address, + destination_address=peer_address, + status=ErrorCode.SUCCESS)) + + await self.expect_evt( + hci.RoleChange(status=ErrorCode.SUCCESS, bd_addr=peer_address, new_role=hci.Role.PERIPHERAL)) + + controller.send_ll(ll.RoleSwitchRequest(source_address=peer_address, destination_address=controller.address)) + + await self.expect_ll( + ll.RoleSwitchResponse(source_address=controller.address, + destination_address=peer_address, + status=ErrorCode.SUCCESS)) + + await self.expect_evt(hci.RoleChange(status=ErrorCode.SUCCESS, bd_addr=peer_address, new_role=hci.Role.CENTRAL)) diff --git a/tools/rootcanal/test/LMP/LIH/BV_142_C.py b/tools/rootcanal/test/LMP/LIH/BV_142_C.py new file mode 100644 index 0000000000..605f1d717d --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_142_C.py @@ -0,0 +1,53 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-142-C [Reject Role Switch Request] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd( + hci.CreateConnection(bd_addr=peer_address, + packet_type=0x7fff, + page_scan_repetition_mode=hci.PageScanRepetitionMode.R0, + allow_role_switch=hci.CreateConnectionRoleSwitch.REMAIN_CENTRAL)) + + await self.expect_evt(hci.CreateConnectionStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.Page(source_address=controller.address, destination_address=peer_address, allow_role_switch=False)) + + controller.send_ll( + ll.PageResponse(source_address=peer_address, destination_address=controller.address, try_role_switch=False)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) + + controller.send_cmd(hci.WriteLinkPolicySettings(connection_handle=acl_connection_handle, + link_policy_settings=0)) + + await self.expect_evt( + hci.WriteLinkPolicySettingsComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + connection_handle=acl_connection_handle)) + + controller.send_ll(ll.RoleSwitchRequest(source_address=peer_address, destination_address=controller.address)) + + await self.expect_ll( + ll.RoleSwitchResponse(source_address=controller.address, + destination_address=peer_address, + status=ErrorCode.ROLE_CHANGE_NOT_ALLOWED)) diff --git a/tools/rootcanal/test/LMP/LIH/BV_143_C.py b/tools/rootcanal/test/LMP/LIH/BV_143_C.py new file mode 100644 index 0000000000..0e6b38af9c --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_143_C.py @@ -0,0 +1,72 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-143-C [Rejected Role Switch Request] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd(hci.WriteScanEnable(scan_enable=hci.ScanEnable.PAGE_SCAN_ONLY)) + + await self.expect_evt(hci.WriteScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + controller.send_ll( + ll.Page(source_address=peer_address, destination_address=controller.address, allow_role_switch=False)) + + await self.expect_evt(hci.ConnectionRequest(bd_addr=peer_address, link_type=hci.ConnectionRequestLinkType.ACL)) + + controller.send_cmd( + hci.AcceptConnectionRequest(bd_addr=peer_address, role=hci.AcceptConnectionRequestRole.REMAIN_PERIPHERAL)) + + await self.expect_evt(hci.AcceptConnectionRequestStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.PageResponse(source_address=controller.address, destination_address=peer_address, try_role_switch=False)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) + + controller.send_cmd( + hci.WriteLinkPolicySettings(connection_handle=acl_connection_handle, + link_policy_settings=hci.LinkPolicy.ENABLE_ROLE_SWITCH)) + + await self.expect_evt( + hci.WriteLinkPolicySettingsComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + connection_handle=acl_connection_handle)) + + controller.send_cmd(hci.SwitchRole(bd_addr=peer_address, role=hci.Role.CENTRAL)) + + await self.expect_evt(hci.SwitchRoleStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll(ll.RoleSwitchRequest(source_address=controller.address, destination_address=peer_address)) + + controller.send_ll( + ll.RoleSwitchResponse(source_address=peer_address, + destination_address=controller.address, + status=ErrorCode.ROLE_CHANGE_NOT_ALLOWED)) + + await self.expect_evt( + hci.RoleChange(status=ErrorCode.ROLE_CHANGE_NOT_ALLOWED, bd_addr=peer_address, + new_role=hci.Role.PERIPHERAL)) + + controller.send_cmd(hci.SwitchRole(bd_addr=peer_address, role=hci.Role.PERIPHERAL)) + + await self.expect_evt(hci.SwitchRoleStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_evt( + hci.RoleChange(status=ErrorCode.ROLE_SWITCH_FAILED, bd_addr=peer_address, new_role=hci.Role.PERIPHERAL)) diff --git a/tools/rootcanal/test/LMP/LIH/BV_144_C.py b/tools/rootcanal/test/LMP/LIH/BV_144_C.py new file mode 100644 index 0000000000..fedd386f98 --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_144_C.py @@ -0,0 +1,41 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-144-C [Rejected Role Switch request at Setup, Peripheral] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd(hci.WriteScanEnable(scan_enable=hci.ScanEnable.PAGE_SCAN_ONLY)) + + await self.expect_evt(hci.WriteScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + controller.send_ll( + ll.Page(source_address=peer_address, destination_address=controller.address, allow_role_switch=False)) + + await self.expect_evt(hci.ConnectionRequest(bd_addr=peer_address, link_type=hci.ConnectionRequestLinkType.ACL)) + + controller.send_cmd( + hci.AcceptConnectionRequest(bd_addr=peer_address, role=hci.AcceptConnectionRequestRole.BECOME_CENTRAL)) + + await self.expect_evt(hci.AcceptConnectionRequestStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.PageResponse(source_address=controller.address, destination_address=peer_address, try_role_switch=True)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) diff --git a/tools/rootcanal/test/LMP/LIH/BV_149_C.py b/tools/rootcanal/test/LMP/LIH/BV_149_C.py new file mode 100644 index 0000000000..a12552dfd6 --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_149_C.py @@ -0,0 +1,68 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-149-C [Reject Role Switch] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd( + hci.CreateConnection(bd_addr=peer_address, + packet_type=0x7fff, + page_scan_repetition_mode=hci.PageScanRepetitionMode.R0, + allow_role_switch=hci.CreateConnectionRoleSwitch.REMAIN_CENTRAL)) + + await self.expect_evt(hci.CreateConnectionStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.Page(source_address=controller.address, destination_address=peer_address, allow_role_switch=False)) + + controller.send_ll( + ll.PageResponse(source_address=peer_address, destination_address=controller.address, try_role_switch=False)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) + + controller.send_cmd( + hci.WriteLinkPolicySettings(connection_handle=acl_connection_handle, + link_policy_settings=hci.LinkPolicy.ENABLE_ROLE_SWITCH)) + + await self.expect_evt( + hci.WriteLinkPolicySettingsComplete(status=ErrorCode.SUCCESS, + num_hci_command_packets=1, + connection_handle=acl_connection_handle)) + + controller.send_cmd(hci.SwitchRole(bd_addr=peer_address, role=hci.Role.CENTRAL)) + + await self.expect_evt(hci.SwitchRoleStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_evt( + hci.RoleChange(status=ErrorCode.ROLE_SWITCH_FAILED, bd_addr=peer_address, new_role=hci.Role.CENTRAL)) + + controller.send_cmd(hci.SwitchRole(bd_addr=peer_address, role=hci.Role.PERIPHERAL)) + + await self.expect_evt(hci.SwitchRoleStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll(ll.RoleSwitchRequest(source_address=controller.address, destination_address=peer_address)) + + controller.send_ll( + ll.RoleSwitchResponse(source_address=peer_address, + destination_address=controller.address, + status=ErrorCode.ROLE_CHANGE_NOT_ALLOWED)) + + await self.expect_evt( + hci.RoleChange(status=ErrorCode.ROLE_CHANGE_NOT_ALLOWED, bd_addr=peer_address, new_role=hci.Role.CENTRAL)) diff --git a/tools/rootcanal/test/LMP/LIH/BV_78_C.py b/tools/rootcanal/test/LMP/LIH/BV_78_C.py new file mode 100644 index 0000000000..42903472e1 --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_78_C.py @@ -0,0 +1,41 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-78-C [Role Switch at Setup, Central] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd( + hci.CreateConnection(bd_addr=peer_address, + packet_type=0x7fff, + page_scan_repetition_mode=hci.PageScanRepetitionMode.R1, + allow_role_switch=hci.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH)) + + await self.expect_evt(hci.CreateConnectionStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.Page(source_address=controller.address, destination_address=peer_address, allow_role_switch=True)) + + controller.send_ll( + ll.PageResponse(source_address=peer_address, destination_address=controller.address, try_role_switch=True)) + + await self.expect_evt( + hci.RoleChange(status=ErrorCode.SUCCESS, bd_addr=peer_address, new_role=hci.Role.PERIPHERAL)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) diff --git a/tools/rootcanal/test/LMP/LIH/BV_79_C.py b/tools/rootcanal/test/LMP/LIH/BV_79_C.py new file mode 100644 index 0000000000..85a3bb0249 --- /dev/null +++ b/tools/rootcanal/test/LMP/LIH/BV_79_C.py @@ -0,0 +1,43 @@ +from dataclasses import dataclass +import hci_packets as hci +import link_layer_packets as ll +import unittest +from hci_packets import ErrorCode +from py.bluetooth import Address +from py.controller import ControllerTest + + +class Test(ControllerTest): + + # LMP/LIH/BV-79-C [Role Switch at Setup, Peripheral] + async def test(self): + # Test parameters. + controller = self.controller + acl_connection_handle = 0xefe + peer_address = Address('11:22:33:44:55:66') + + controller.send_cmd(hci.WriteScanEnable(scan_enable=hci.ScanEnable.PAGE_SCAN_ONLY)) + + await self.expect_evt(hci.WriteScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + controller.send_ll( + ll.Page(source_address=peer_address, destination_address=controller.address, allow_role_switch=True)) + + await self.expect_evt(hci.ConnectionRequest(bd_addr=peer_address, link_type=hci.ConnectionRequestLinkType.ACL)) + + controller.send_cmd( + hci.AcceptConnectionRequest(bd_addr=peer_address, role=hci.AcceptConnectionRequestRole.BECOME_CENTRAL)) + + await self.expect_evt(hci.AcceptConnectionRequestStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1)) + + await self.expect_ll( + ll.PageResponse(source_address=controller.address, destination_address=peer_address, try_role_switch=True)) + + await self.expect_evt(hci.RoleChange(status=ErrorCode.SUCCESS, bd_addr=peer_address, new_role=hci.Role.CENTRAL)) + + await self.expect_evt( + hci.ConnectionComplete(status=ErrorCode.SUCCESS, + connection_handle=acl_connection_handle, + bd_addr=peer_address, + link_type=hci.LinkType.ACL, + encryption_enabled=hci.Enable.DISABLED)) diff --git a/tools/rootcanal/test/main.py b/tools/rootcanal/test/main.py index 66b8999c27..bbc6383800 100644 --- a/tools/rootcanal/test/main.py +++ b/tools/rootcanal/test/main.py @@ -41,6 +41,14 @@ tests = [ 'LL.DDI.SCN.BV_18_C', 'LL.DDI.SCN.BV_19_C', 'LL.DDI.SCN.BV_79_C', + 'LMP.LIH.BV_01_C', + 'LMP.LIH.BV_02_C', + 'LMP.LIH.BV_78_C', + 'LMP.LIH.BV_79_C', + 'LMP.LIH.BV_142_C', + 'LMP.LIH.BV_143_C', + 'LMP.LIH.BV_144_C', + 'LMP.LIH.BV_149_C', ] if __name__ == "__main__": |