Skip to content

Commit 72bdcbf

Browse files
committed
tests: bluetooth: classic: Add test suite l2cap.
IUT works as a l2cap server with basic mode. The peer device, l2cap client with basic mode, is a PC with running `bumble` on it. This test only performs the function of L2CAP basic mode. Support multiple l2cap enerties in new shell l2cap_br, which may support more test function. In the test suite , there are two groups in test cases. Group 1 Including case1-case8 focuses on connection and disconnection around l2cap. The impact of active and passive acl connectivity, disconnectivity and authentication as well as disconnection from ACL without l2cap disconnect is tested. Group2 Including case9-case14 revolves around the basic parameters of L2CAPserver configuration, data transfer. Case 9: Test l2cap connection with max MTU(0xffff). But the max mtu which the stack supports is (CONFIG_BT_BUF_ACL_RX_SIZE - 4U = 196). Case 10: Test l2cap connection with min MTU(0x30). Case 11: Test l2cap connection with invaild PSM. Case 12: Test l2cap multi_channel connection and data tranfer. Case 13: Stress Test. Repeat l2cap connect, disconnect operation. Case 14: Stress Test. Repeat data transfer operation in a single connection. In Case 13 and 14, if enlarging STRESS_TEST_MAX_COUNT and test fail, you can enlarge timeout in testcase.yml. test_l2cap_server.py is a file containing real test cases. test_l2cap_common.py is a common file. It encapsulates some test function functions that are commonly used for test cases. Signed-off-by: Cheng Chang <[email protected]>
1 parent b2b7a46 commit 72bdcbf

10 files changed

+1625
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
set(NO_QEMU_SERIAL_BT_SERVER 1)
5+
6+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
7+
project(bluetooth)
8+
9+
FILE(GLOB app_sources src/*.c)
10+
target_sources(app PRIVATE ${app_sources})
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
.. _bluetooth_classic_sdp_clinet_tests:
2+
3+
Bluetooth Classic L2CAP BASE MODE Tests
4+
##################################
5+
6+
Overview
7+
********
8+
9+
This test suite uses ``bumble`` for testing Bluetooth Classic communication between a host
10+
PC (running :ref:`Twister <twister_script>`) and a device under test (DUT) running Zephyr.
11+
12+
Prerequisites
13+
*************
14+
15+
The test suite has the following prerequisites:
16+
17+
* The ``bumble`` library installed on the host PC.
18+
The Bluetooth Classic controller on PC side is required. Refer to getting started of `bumble`_
19+
for details.
20+
21+
The HCI transport for ``bumble`` can be configured as follows:
22+
23+
* A specific `hci transport`_ can be provided by using the ``--hci-transport`` test suite argument
24+
(i.e. run ``twister`` with the ``--pytest-args=--hci-transport=usb:0`` argument to use the
25+
``usb:0`` as hci transport for ``bumble``).
26+
27+
Building and Running
28+
********************
29+
30+
Running on mimxrt1170_evk@B/mimxrt1176/cm7
31+
==========================================
32+
33+
Running the test suite on :ref:`mimxrt1170_evk` relies on configuration of ``bumble``.
34+
35+
On the host PC, a HCI transport needs to be required. Refer to `bumble platforms`_ page of
36+
``bumble`` for details.
37+
38+
For example, on windows, a PTS dongle is used. After `WinUSB driver`_ has been installed,
39+
the HCI transport would be USB transport interface ``usb:<index>``.
40+
41+
If the HCI transport is ``usb:0`` and debug console port is ``COM4``, the test suite can be
42+
launched using Twister:
43+
44+
.. code-block:: shell
45+
46+
west twister -v -p mimxrt1170_evk@B/mimxrt1176/cm7 --device-testing --device-serial COM4 -T tests/bluetooth/classic/l2cap -O l2cap_br --force-platform --west-flash --west-runner=jlink --pytest-args=--hci-transport=usb:0
47+
48+
Running on Hardware
49+
===================
50+
51+
Running the test suite on hardware requires a HCI transport connected to the host PC.
52+
53+
The test suite can be launched using Twister. Below is an example for running on the
54+
:zephyr:board:`mimxrt1170_evk@B/mimxrt1176/cm7`:
55+
56+
.. code-block:: shell
57+
58+
west twister -v -p mimxrt1170_evk@B/mimxrt1176/cm7 --device-testing --device-serial COM4 -T tests/bluetooth/classic/l2cap -O l2cap_br --force-platform --west-flash --west-runner=jlink --pytest-args=--hci-transport=usb:0
59+
60+
.. _bumble:
61+
https://google.github.io/bumble/getting_started.html
62+
63+
.. _hci transport:
64+
https://google.github.io/bumble/transports/index.html
65+
66+
.. _bumble platforms:
67+
https://google.github.io/bumble/platforms/index.html
68+
69+
.. _WinUSB driver:
70+
https://google.github.io/bumble/platforms/windows.html
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#select NXP NW612 Chipset
2+
CONFIG_BT_NXP_NW612=y
3+
4+
CONFIG_BT_SETTINGS=n
5+
CONFIG_FLASH=n
6+
CONFIG_FLASH_MAP=n
7+
CONFIG_NVS=n
8+
CONFIG_SETTINGS=n
9+
10+
CONFIG_ENTROPY_GENERATOR=y
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright 2024 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/ {
8+
chosen {
9+
zephyr,sram = &dtcm;
10+
};
11+
};
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CONFIG_BT=y
2+
CONFIG_BT_CLASSIC=y
3+
CONFIG_BT_SHELL=y
4+
CONFIG_LOG=y
5+
CONFIG_DEBUG=y
6+
CONFIG_ZTEST=y
7+
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
8+
CONFIG_BT_L2CAP_RET=y
9+
CONFIG_BT_L2CAP_FC=y
10+
CONFIG_BT_L2CAP_ENH_RET=y
11+
CONFIG_BT_L2CAP_STREAM=y
12+
CONFIG_BT_L2CAP_FCS=y
13+
CONFIG_BT_L2CAP_EXT_WIN_SIZE=y
14+
CONFIG_BT_L2CAP_MAX_WINDOW_SIZE=5
15+
CONFIG_BT_DEVICE_NAME="L2CAP_BR"
16+
CONFIG_BT_CREATE_CONN_TIMEOUT=30
17+
CONFIG_BT_PAGE_TIMEOUT=0xFFFF
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2024 NXP
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import logging
6+
import re
7+
8+
import pytest
9+
from twister_harness import DeviceAdapter, Shell
10+
11+
logger = logging.getLogger(__name__)
12+
13+
from test_l2cap_common import L2CAP_SERVER_PSM,Mode
14+
15+
def pytest_addoption(parser) -> None:
16+
"""Add local parser options to pytest."""
17+
parser.addoption('--hci-transport', default=None, help='Configuration HCI transport for bumble')
18+
19+
20+
@pytest.fixture(name='initialize', scope='session')
21+
def fixture_initialize(request, shell: Shell, dut: DeviceAdapter):
22+
"""Session initializtion"""
23+
# Get HCI transport for bumble
24+
hci = request.config.getoption('--hci-transport')
25+
26+
if hci is None:
27+
for fixture in dut.device_config.fixtures:
28+
if fixture.startswith('usb_hci:'):
29+
hci = fixture.split(sep=':', maxsplit=1)[1]
30+
break
31+
32+
assert hci is not None
33+
34+
lines = shell.exec_command("bt init")
35+
lines = dut.readlines_until("Bluetooth initialized")
36+
regex = r"Identity: (?P<bd_addr>(.*\s\(.*\)))"
37+
bd_addr = None
38+
for line in lines:
39+
logger.info(f"Shell log {line}")
40+
m = re.search(regex, line)
41+
if m:
42+
bd_addr = m.group('bd_addr')
43+
44+
if bd_addr is None:
45+
logger.error('Fail to get IUT BD address')
46+
raise AssertionError
47+
48+
lines = shell.exec_command("br pscan on")
49+
lines = shell.exec_command("br iscan on")
50+
logger.info('initialized')
51+
52+
lines = shell.exec_command(f"l2cap_br register {format(L2CAP_SERVER_PSM, 'x')} {Mode}")
53+
logger.info("l2cap server register")
54+
return hci, bd_addr
55+
56+
57+
@pytest.fixture
58+
def l2cap_br_dut(initialize):
59+
logger.info('Start running testcase')
60+
yield initialize
61+
logger.info('Done')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Copyright 2025 NXP
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import re
6+
import asyncio
7+
import logging
8+
import sys
9+
10+
from enum import Enum
11+
from bumble.core import BT_BR_EDR_TRANSPORT, ProtocolError
12+
from bumble.pairing import PairingConfig, PairingDelegate
13+
from bumble.l2cap import (
14+
ClassicChannelSpec,
15+
)
16+
from bumble.device import Device, DeviceConfiguration
17+
from bumble.hci import Address, HCI_Write_Page_Timeout_Command,HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR
18+
from bumble.snoop import BtSnooper
19+
from bumble.transport import open_transport_or_link
20+
from twister_harness import DeviceAdapter, Shell
21+
22+
logger = logging.getLogger(__name__)
23+
24+
L2CAP_SERVER_PSM=0x1001
25+
Mode="basic"
26+
L2CAP_CHAN_IUT_ID=0
27+
MAX_MTU=0xffff #65535
28+
MIN_MTU=0x30 #48
29+
STRESS_TEST_MAX_COUNT=50
30+
31+
class Delegate(PairingDelegate):
32+
def __init__(
33+
self,
34+
dut,
35+
io_capability,
36+
):
37+
super().__init__(
38+
io_capability,
39+
)
40+
self.dut = dut
41+
42+
async def device_power_on(device) -> None:
43+
while True:
44+
try:
45+
await device.power_on()
46+
break
47+
except Exception:
48+
continue
49+
50+
async def wait_for_shell_response(dut, message):
51+
found = False
52+
lines = []
53+
try:
54+
while found is False:
55+
read_lines = dut.readlines()
56+
for line in read_lines:
57+
if message in line:
58+
found = True
59+
break
60+
lines = lines + read_lines
61+
await asyncio.sleep(0.1)
62+
except Exception as e:
63+
logger.error(f'{e}!', exc_info=True)
64+
raise e
65+
return found, lines
66+
67+
async def send_cmd_to_iut(shell, dut, cmd, parse=None, wait = True):
68+
found = False
69+
lines = shell.exec_command(cmd)
70+
if wait:
71+
if parse is not None:
72+
found, lines = await wait_for_shell_response(dut, parse)
73+
else:
74+
found = True
75+
else:
76+
found = False
77+
if parse!=None:
78+
for line in lines:
79+
if parse in line:
80+
found = True
81+
break
82+
else:
83+
found = True
84+
logger.info(f'{lines}')
85+
assert found is True
86+
return lines
87+
88+
async def bumble_acl_connect(shell, dut, device, target_address):
89+
connection = None
90+
try:
91+
connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
92+
logger.info(f'=== Connected to {connection.peer_address}!')
93+
except Exception as e:
94+
logger.error(f'Fail to connect to {target_address}!')
95+
raise e
96+
return connection
97+
98+
async def bumble_acl_disconnect(shell, dut, device, connection):
99+
await device.disconnect(connection, reason=HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR)
100+
found, lines = await wait_for_shell_response(dut, "Disconnected:")
101+
logger.info(f'lines : {lines}')
102+
assert found is True
103+
return found, lines
104+
105+
async def bumble_l2cap_disconnect(shell, dut, incoming_channel, iut_id=L2CAP_CHAN_IUT_ID):
106+
try:
107+
await incoming_channel.disconnect()
108+
except Exception as e:
109+
logger.error(f'Fail to send l2cap disconnect command!')
110+
raise e
111+
assert incoming_channel.disconnection_result is None
112+
113+
found, lines = await wait_for_shell_response(dut, f"Channel {iut_id} disconnected")
114+
assert found is True

0 commit comments

Comments
 (0)