zephyr/tests/bluetooth/classic/rfcomm_c/pytest/test_rfcomm_c.py
Jiawei Yang ff112b5e05 tests: Bluetooth: BR: Add test suite RFCOMM_client
IUT works as an RFCOMM Client. The peer device, RFCOMM server, is a PC
running bumble.

Add following test cases:

Case 6, RFCOMM MTU Size Data Send/Receive Test. To verify that the RFCOMM
client can correctly send and receive data packets that are exactly equal
to the negotiated MTU size.

Case 7, RFCOMM Data Transfer Exceeding MTU Size Test. To verify that the
RFCOMM client can reject sending and receiving data packets that exceed
the negotiated MTU size.

Case 8, RFCOMM Disconnect and Reconnect Test. To verify that the RFCOMM
client can properly handle a normal RFCOMM disconnection and successfully
reestablish the connection.

Case 9, RFCOMM Recovery After ACL Disconnection Test. To verify that the
RFCOMM client can properly recover when the underlying ACL connection is
abruptly disconnected, by reestablishing both ACL and RFCOMM connections.

Signed-off-by: Jiawei Yang <jiawei.yang_1@nxp.com>
2025-07-10 10:12:20 -05:00

1031 lines
42 KiB
Python

# Copyright 2025 NXP
#
# SPDX-License-Identifier: Apache-2.0
import asyncio
import logging
import sys
import bumble
import rfcomm_utility
from bumble import hci
from bumble.core import BT_BR_EDR_TRANSPORT, DeviceClass, InvalidStateError
from bumble.device import DEVICE_DEFAULT_INQUIRY_LENGTH, Device
from bumble.hci import Address, HCI_Write_Page_Timeout_Command
from bumble.rfcomm import DLC, RFCOMM_MCC_PN, MccType, RFCOMM_Frame, Server
from bumble.snoop import BtSnooper
from bumble.transport import open_transport_or_link
from rfcomm_utility import send_value, setup_logger_capture, wait_mux_response
from twister_harness import DeviceAdapter, Shell
logger = logging.getLogger(__name__)
logger_capture = setup_logger_capture(logger)
dlc_9 = None
dlc_7 = None
rfcomm_mux = None
original_dlc_accept_api = DLC.accept
# power on dongle
async def device_power_on(device) -> None:
while True:
try:
await device.power_on()
break
except Exception:
continue
# wait for shell response
async def _wait_for_shell_response(dut, response, max_wait_sec=10):
"""
_wait_for_shell_response() is used to wait for shell response.
It will return after finding a specific 'response' or waiting long enough.
:param dut:
:param response: shell response that you want to monitor.
:param max_wait_sec: maximum waiting time
:return: found: whether the 'response' is found; lines: DUT shell response
"""
found = False
lines = []
try:
for _ in range(0, max_wait_sec):
read_lines = dut.readlines()
for line in read_lines:
if response in line:
found = True
break
lines = lines + read_lines
await asyncio.sleep(1)
logger.info(f'{str(lines)}')
except Exception as e:
logger.error(f'{e}!', exc_info=True)
raise e
return found, lines
# interact between script and DUT
async def send_cmd_to_iut(
shell, dut, cmd, response=None, expect_to_find_resp=True, max_wait_sec=20, shell_ret=False
):
"""
send_cmd_to_iut() is used to send shell cmd to DUT and monitor the response.
It can choose whether to monitor the shell response of DUT.
Use 'expect_to_find_resp' to set whether to expect the response to contain certain 'response'.
'max_wait_sec' indicates the maximum waiting time.
For 'expect_to_find_resp=False', this is useful
because we need to wait long enough to get enough response
to more accurately judge that the response does not contain specific characters.
:param shell:
:param dut:
:param cmd: shell cmd sent to DUT
:param response: shell response that you want to monitor.
'None' means not to monitor any response.
:param expect_to_find_resp: set whether to expect the response to contain certain 'response'
:param max_wait_sec: maximum monitoring time
:param shell_ret: flag to indicate whether to check shell command return directly
:return: DUT shell response
"""
found = False
lines = ''
shell_cmd_ret = shell.exec_command(cmd)
if response is not None:
if shell_ret:
shell_cmd_ret_str = str(shell_cmd_ret)
if response in shell_cmd_ret_str:
found = True
lines = shell_cmd_ret_str
else:
found, lines = await _wait_for_shell_response(dut, response, max_wait_sec)
else:
found = True
assert found is expect_to_find_resp
return lines
# set limited discoverab mode of dongle
async def set_limited_discoverable(device, discoverable=True):
# Read current class of device
response = await device.send_command(
hci.HCI_Command(
op_code=0x0C23, # Read Class of Device
parameters=b'',
)
)
current_cod = response.return_parameters.class_of_device
if discoverable:
# set Limited Discoverable Mode (bit 13)
new_cod = (current_cod | 0x2000).to_bytes(3, byteorder='little')
# Limited Inquiry Access Code(LIAC) = 0x9E8B00
iac = hci.HCI_LIMITED_DEDICATED_INQUIRY_LAP.to_bytes(3, byteorder='little')
else:
mask = ~0x2000
new_cod = (current_cod & mask).to_bytes(3, byteorder='little')
# General Inquiry Access Code(GIAC) = 0x9E8B33
iac = hci.HCI_GENERAL_INQUIRY_LAP.to_bytes(3, byteorder='little')
await device.send_command(
hci.HCI_Command(
op_code=0x0C24, # Write Class of Device
parameters=new_cod,
)
)
await device.send_command(
hci.HCI_Command(
op_code=0x0C3A, # Write Current IAC LAP
parameters=bytes([0x01]) + iac, # num_current_iac=1, iac_lap
)
)
device.discoverable = discoverable
# dongle limited discovery
async def start_limited_discovery(device):
await device.send_command(
hci.HCI_Write_Inquiry_Mode_Command(inquiry_mode=hci.HCI_EXTENDED_INQUIRY_MODE),
check_result=True,
)
response = await device.send_command(
hci.HCI_Inquiry_Command(
lap=hci.HCI_LIMITED_DEDICATED_INQUIRY_LAP,
inquiry_length=DEVICE_DEFAULT_INQUIRY_LENGTH,
num_responses=0, # Unlimited number of responses.
)
)
if response.status != hci.HCI_Command_Status_Event.PENDING:
device.discovering = False
raise hci.HCI_StatusError(response)
device.auto_restart_inquiry = False
device.discovering = True
# device listener for receiving scan results
class DiscoveryListener(Device.Listener):
def __init__(self):
self.discovered_addresses = set()
def on_inquiry_result(self, address, class_of_device, data, rssi):
DeviceClass.split_class_of_device(class_of_device)
found_address = str(address).replace(r'/P', '')
logger.info(f'Found addr: {found_address}')
self.discovered_addresses.add(found_address)
def has_found_target_addr(self, target_addr):
return str(target_addr).upper() in self.discovered_addresses
# establish_dlc
async def establish_dlc(rfcomm_mux, channel, send_PN=True):
logger.info(f'Establish DLC for channel {channel}...')
try:
session = await rfcomm_mux.open_dlc(channel, send_PN=send_PN)
# assert await wait_mux_response(rfcomm_utility.logger_capture, 'Receive PN')
except bumble.core.ConnectionError as error:
logger.error(f'DLC open failed: {error}')
await rfcomm_mux.disconnect()
raise error
def dlc_echo_back(data):
logger.info(f'<<< [Channel: {int(session.dlci / 2)}] RFCOMM Data received: {data.hex()}')
if data == send_value:
logger.info('Data send pass')
else:
logger.info('Data send fail')
session.write(data)
if session is not None:
session.sink = dlc_echo_back
return session
# for dlc establishment response
def on_rfcomm_dlc(session):
global dlc_9, dlc_7
if session is None:
return
if int(session.dlci / 2) == 9:
dlc_9 = session
elif int(session.dlci / 2) == 7:
dlc_7 = session
else:
logger.error('channel should be either 9 or 7')
return
logger.info(f'<<< [Channel: {int(session.dlci / 2)}] created')
# dlc_echo_back
def dlc_echo_back(data):
logger.info(f'<<< [Channel: {int(session.dlci / 2)}] RFCOMM Data received: {data.hex()}')
if data == send_value:
logger.info('Data send pass')
else:
logger.info('Data send fail')
session.write(data)
session.sink = dlc_echo_back
# for multiplexer establishment response
def on_multiplexer_start(multiplexer):
global rfcomm_mux
rfcomm_mux = multiplexer
logger.info("New multiplexer started")
async def tc_rfcomm_c_1(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Client Command Transfer'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
channel_9 = 9
channel_7 = 7
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
await send_cmd_to_iut(shell, dut, "rfcomm_s register 7") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_7)
# Test Start
logger.info('Step 1: Establish DLC on channel 9 and 7')
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", "connected")
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 7", "connected")
logger.info('Step 2: Response to RLS Command, RPN Command, Test Command, and NSC response')
rfcomm_mux.send_rls_command(channel_9)
assert await wait_mux_response(rfcomm_utility.logger_capture, 'Receive RLS response'), (
"Failed to receive RLS response"
)
rfcomm_mux.send_rpn_command(channel_9)
assert await wait_mux_response(rfcomm_utility.logger_capture, 'Receive RPN response'), (
"Failed to receive RPN response"
)
rfcomm_mux.send_test_command(rfcomm_utility.mcc_test_data)
assert await wait_mux_response(rfcomm_utility.logger_capture, 'MCC TEST pass'), (
"Failed to receive Test response"
)
rfcomm_mux.send_bad_command(channel_9)
assert await wait_mux_response(rfcomm_utility.logger_capture, 'Receive NSC response'), (
"Failed to receive NSC response"
)
logger.info('Step 3: Disconnect DLC')
await dlc_9.disconnect()
found, _ = await _wait_for_shell_response(dut, "disconnected")
assert found, "DUT did not report DLC disconnection"
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1", "Unable to send", shell_ret=True)
await send_cmd_to_iut(shell, dut, "rfcomm_s send 7 1")
assert await wait_mux_response(logger_capture, 'Data send pass'), (
"Failed to receive data from DUT"
)
logger.info('Step 4: Disconnect session')
await rfcomm_mux.disconnect()
found, _ = await _wait_for_shell_response(dut, "disconnected")
assert found, "DUT did not report DLC disconnection"
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1", "Unable to send", shell_ret=True)
await send_cmd_to_iut(shell, dut, "rfcomm_s send 7 1", "Unable to send", shell_ret=True)
async def tc_rfcomm_c_2(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Client with Credit Based Flow Control'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
channel_9 = 9
channel_7 = 7
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
await send_cmd_to_iut(shell, dut, "rfcomm_s register 7") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_7)
# Test Start
logger.info('Step 1: Establish DLC on channel 9 and 7')
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", "connected")
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 7", "connected")
logger.info('Step 2: Perform information transfer using credit based flow control')
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1")
assert await wait_mux_response(logger_capture, 'Data send pass'), (
"Failed to receive data on channel 9 from DUT"
)
await send_cmd_to_iut(shell, dut, "rfcomm_s send 7 1")
assert await wait_mux_response(logger_capture, 'Data send pass'), (
"Failed to receive data on channel 7 from DUT"
)
# Sending data from dongle to DUT
dlc_9.write(send_value)
dlc_7.write(send_value)
found, _ = await _wait_for_shell_response(dut, "Incoming data")
assert found, "DUT did not report receiving data from dongle"
logger.info('Step 3: Initiate disconnection of the DLCs')
await send_cmd_to_iut(shell, dut, "rfcomm_s disconnect 9", "disconnect")
assert await wait_mux_response(rfcomm_utility.logger_capture, 'DISC received on dlc=18')
logger.info('Step 4: Shutdown the RFCOMM session')
await send_cmd_to_iut(shell, dut, "rfcomm_s disconnect 7", "disconnect")
assert await wait_mux_response(rfcomm_utility.logger_capture, 'DISC received on dlc=0')
# Verify resources are released
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1", "Unable to send", shell_ret=True)
await send_cmd_to_iut(shell, dut, "rfcomm_s send 7 1", "Unable to send", shell_ret=True)
async def tc_rfcomm_c_3(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Client with BR Connection Disconnection'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
channel_9 = 9
channel_7 = 7
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
await send_cmd_to_iut(shell, dut, "rfcomm_s register 7") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_7)
# Test Start
logger.info('Step 1: BR connection disconnection')
# Disconnect BR connection
await connection.disconnect()
logger.info('Step 2: Start DLC establishment process')
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", "Not connected", shell_ret=True)
logger.info('Step 3: Verify DLC establishment is rejected')
# Try to send data to verify resources are released properly
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1", "Unable to send", shell_ret=True)
async def tc_rfcomm_c_4(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Client with Aggregate Flow Control'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
channel_9 = 9
channel_7 = 7
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
await send_cmd_to_iut(shell, dut, "rfcomm_s register 7") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_7)
# Test Start
logger.info('Step 1: Establish DLC on channel 9 and 7 with aggregate flow control enabled')
def accept(self) -> None:
# Simulate an rfcomm server device that does not support CFC
if self.state != DLC.State.INIT:
raise InvalidStateError('invalid state')
pn = RFCOMM_MCC_PN(
dlci=self.dlci,
cl=0x00,
priority=7,
ack_timer=0,
max_frame_size=self.rx_max_frame_size,
max_retransmissions=0,
initial_credits=self.rx_initial_credits,
)
mcc = RFCOMM_Frame.make_mcc(mcc_type=MccType.PN, c_r=0, data=bytes(pn))
logger.debug(f'>>> PN Response: {pn}')
self.send_frame(RFCOMM_Frame.uih(c_r=self.c_r, dlci=0, information=mcc))
self.change_state(DLC.State.CONNECTING)
DLC.accept = accept
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", "connected")
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 7", "connected")
logger.info('Step 2: DUT transfer data after received FCOFF command from Tester')
rfcomm_mux.send_fcoff_command()
assert await wait_mux_response(rfcomm_utility.logger_capture, 'Received FCOFF response')
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1")
assert not await wait_mux_response(logger_capture, 'Data send pass', max_wait_sec=5), (
"DUT should not send data"
)
logger.info('Step 3: The blocked data can be sent after FCON command')
rfcomm_mux.send_fcon_command()
assert await wait_mux_response(rfcomm_utility.logger_capture, 'Received FCON response')
assert await wait_mux_response(logger_capture, 'Data send pass')
logger.info('Step 4: Initiate disconnection of the DLCs')
await send_cmd_to_iut(shell, dut, "rfcomm_s disconnect 9", "disconnect")
assert await wait_mux_response(rfcomm_utility.logger_capture, 'DISC received on dlc=18')
logger.info('Step 5: Shutdown the RFCOMM session')
await send_cmd_to_iut(shell, dut, "rfcomm_s disconnect 7", "disconnect")
assert await wait_mux_response(rfcomm_utility.logger_capture, 'DISC received on dlc=0')
# Verify resources are released
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1", "Unable to send", shell_ret=True)
await send_cmd_to_iut(shell, dut, "rfcomm_s send 7 1", "Unable to send", shell_ret=True)
async def tc_rfcomm_c_5(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Client with Failed PN Response'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
channel_9 = 9
channel_7 = 7
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
await send_cmd_to_iut(shell, dut, "rfcomm_s register 7") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_7)
# Test Start
def accept(self) -> None:
if self.state != DLC.State.INIT:
raise InvalidStateError('invalid state')
logger.info('Ignore PN cmd')
self.change_state(DLC.State.CONNECTING)
DLC.accept = accept
logger.info('Step 1: Start a DLC from the client device')
await send_cmd_to_iut(
shell, dut, "rfcomm_s connect 9", " connected", expect_to_find_resp=False
)
logger.info('Step 2: Simulate a failure to receive PN response from the remote device')
logger.info('Step 3: Verify DLC establishment is rejectede')
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1", "Unable to send", shell_ret=True)
async def tc_rfcomm_c_6(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM MTU Size Data Send/Receive Test'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
global send_value
DLC.accept = original_dlc_accept_api
channel_9 = 9
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
# Test Start
logger.info('Step 1: Initialize RFCOMM client and establish connection with the server')
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", " connected")
logger.info('Step 2: Generate a test data packet with size equal to the MTU size')
dlc_9.write(bytes([0xFF]))
data_length = int(dlc_9.mtu)
send_value = bytes([0xFF] * data_length)
logger.info('Step 3: Send the test data packet from client to server')
await send_cmd_to_iut(shell, dut, f"rfcomm_s send 9 {data_length}")
assert await wait_mux_response(logger_capture, 'Data send pass'), (
"Failed to receive data on channel 9 from DUT"
)
logger.info('Step 4: Request server to send back a data packet of MTU size')
# Server will send back the same data length as received automatically
logger.info('Step 5: Receive and verify the data packet from server')
found, _ = await _wait_for_shell_response(dut, f"Data length: {data_length}")
assert found is True, "Failed to send data on channel 9 to DUT"
async def tc_rfcomm_c_7(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Data Transfer Exceeding MTU Size Test'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
global send_value
DLC.accept = original_dlc_accept_api
channel_9 = 9
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
# Test Start
logger.info('Step 1: Initialize RFCOMM client and establish connection with the server')
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", " connected")
logger.info('Step 2: Generate a test data packet with size 3 times the MTU size')
dlc_9.write(bytes([0xFF]))
data_length = int(dlc_9.mtu * 3)
send_value = bytes([0xFF] * data_length)
logger.info('Step 3: Send the test data packet from client to server')
await send_cmd_to_iut(
shell, dut, f"rfcomm_s send 9 {data_length}", "Unable to send", shell_ret=True
)
async def tc_rfcomm_c_8(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Disconnect and Reconnect Test'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
DLC.accept = original_dlc_accept_api
channel_9 = 9
global send_value
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
# Test Start
logger.info('Step 1: Initialize RFCOMM client and establish connection with the server')
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", " connected")
logger.info('Step 2: Initiate a controlled RFCOMM disconnection from the client side')
await send_cmd_to_iut(shell, dut, "rfcomm_s disconnect 9", "disconnect")
logger.info('Step 3: Initiate RFCOMM reconnection from the client to the server')
await asyncio.sleep(1)
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", " connected")
logger.info('Step 4: Exchange data to confirm the new connection is working properly')
send_value = bytes([0xFF])
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1")
assert await wait_mux_response(logger_capture, 'Data send pass'), (
"Failed to receive data on channel 9 from DUT"
)
async def tc_rfcomm_c_9(hci_port, shell, dut, address, snoop_file) -> None:
case_name = 'RFCOMM Recovery After ACL Disconnection Test'
logger.info(f'<<< Start {case_name} ...')
dut_address = address.split(" ")[0]
async with await open_transport_or_link(hci_port) as hci_transport:
# Init Dongle State
device = Device.with_hci(
'Bumble',
Address('F0:F1:F2:F3:F4:F5'),
hci_transport.source,
hci_transport.sink,
)
device.classic_enabled = True
device.le_enabled = False
device.listener = DiscoveryListener()
device.host.snooper = BtSnooper(snoop_file)
await device_power_on(device)
await device.set_discoverable(True)
await device.set_connectable(True)
await device.send_command(HCI_Write_Page_Timeout_Command(page_timeout=0xFFFF))
# Initial Condition
DLC.accept = original_dlc_accept_api
channel_9 = 9
global send_value
logger.info('Initial Condition: Set DUT state...')
await send_cmd_to_iut(shell, dut, "br pscan on") # set to connectable
await send_cmd_to_iut(shell, dut, "br iscan on") # set to general discoverable
await send_cmd_to_iut(shell, dut, "rfcomm_s register 9") # create RFCOMM server
# Connecting
logger.info(f'Initial Condition: establish be connection to {dut_address}...')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
# Request authentication
logger.info('Initial Condition: Authenticating...')
await connection.authenticate()
# Enable encryption
logger.info('Initial Condition: Enabling encryption...')
await connection.encrypt()
# Create RFCOMM server
logger.info('Initial Condition: Create RFCOMM client...')
rfcomm_server = Server(device)
rfcomm_server.on('start', on_multiplexer_start)
rfcomm_server.listen(on_rfcomm_dlc, channel=channel_9)
# Test Start
logger.info('Step 1: Initialize RFCOMM client and establish connection with the server')
await asyncio.sleep(1)
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", " connected")
logger.info('Step 2: Force an abrupt ACL disconnection')
await send_cmd_to_iut(shell, dut, "bt disconnect", "disconnect")
logger.info('Step 3: Verify that both ACL and RFCOMM connections are terminated')
await asyncio.sleep(1)
send_value = bytes([0xFF])
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1", "Unable to send", shell_ret=True)
logger.info('Step 4: Initiate ACL connection reestablishment from the client')
connection = await device.connect(dut_address, transport=BT_BR_EDR_TRANSPORT)
found, _ = await _wait_for_shell_response(dut, "Connected")
assert found is True, "DUT did not report connection established"
await connection.authenticate()
await connection.encrypt()
logger.info('Step 5: Initialize RFCOMM client and establish connection with the server')
await send_cmd_to_iut(shell, dut, "rfcomm_s connect 9", " connected")
logger.info('Step 6: Exchange data to confirm the new connections are working properly')
await send_cmd_to_iut(shell, dut, "rfcomm_s send 9 1")
assert await wait_mux_response(logger_capture, 'Data send pass'), (
"Failed to receive data on channel 9 from DUT"
)
class TestRFCOMM:
def test_rfcomm_c_1(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Client Command Transfer."""
logger.info(f'RFCOMM-C-1 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_1(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_2(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Client with Credit Based Flow Control."""
logger.info(f'RFCOMM-C-2 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_2(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_3(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Client with BR Connection Disconnection."""
logger.info(f'RFCOMM-C-3 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_3(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_4(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Client with Aggregate Flow Control."""
logger.info(f'RFCOMM-C-4 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_4(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_5(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Client with Failed PN Response."""
logger.info(f'RFCOMM-C-5 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_5(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_6(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM MTU Size Data Send/Receive Test."""
logger.info(f'RFCOMM-C-6 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_6(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_7(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Data Transfer Exceeding MTU Size Test."""
logger.info(f'RFCOMM-C-7 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_7(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_8(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Disconnect and Reconnect Test."""
logger.info(f'RFCOMM-C-8 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_8(hci, shell, dut, iut_address, snoop_file))
def test_rfcomm_c_9(self, shell: Shell, dut: DeviceAdapter, device_under_test):
"""RFCOMM Recovery After ACL Disconnection Test."""
logger.info(f'RFCOMM-C-9 {device_under_test}')
hci, iut_address = device_under_test
with open(f"bumble_hci_{sys._getframe().f_code.co_name}.log", "wb") as snoop_file:
asyncio.run(tc_rfcomm_c_9(hci, shell, dut, iut_address, snoop_file))