1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/bluetooth/bluetooth.h>
9 #include <zephyr/bluetooth/conn.h>
10 #include <zephyr/bluetooth/l2cap.h>
11 #include <zephyr/logging/log.h>
12
13 #include "testlib/conn.h"
14 #include "testlib/scan.h"
15
16 #include "babblekit/flags.h"
17 #include "babblekit/testcase.h"
18
19 /* local includes */
20 #include "data.h"
21
22 LOG_MODULE_REGISTER(dut, CONFIG_APP_LOG_LEVEL);
23
24 #define NUM_TESTERS CONFIG_BT_MAX_CONN
25
26 /* Build with the minimum possible amount of RX buffers */
27 BUILD_ASSERT(BT_BUF_ACL_RX_COUNT >= (CONFIG_BT_MAX_CONN + 1));
28
29 struct tester {
30 size_t sdu_count;
31 struct bt_conn *conn;
32 struct bt_l2cap_le_chan le_chan;
33 };
34
35 static struct tester testers[NUM_TESTERS];
36
get_tester(struct bt_conn * conn)37 static struct tester *get_tester(struct bt_conn *conn)
38 {
39 for (size_t i = 0; i < ARRAY_SIZE(testers); i++) {
40 if (testers[i].conn == conn) {
41 return &testers[i];
42 }
43 }
44
45 return NULL;
46 }
47
sent_cb(struct bt_l2cap_chan * chan)48 static void sent_cb(struct bt_l2cap_chan *chan)
49 {
50 TEST_FAIL("Tester should not send data");
51 }
52
recv_cb(struct bt_l2cap_chan * chan,struct net_buf * buf)53 static int recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
54 {
55 char addr[BT_ADDR_LE_STR_LEN];
56 struct tester *tester = get_tester(chan->conn);
57
58 tester->sdu_count += 1;
59
60 bt_addr_le_to_str(bt_conn_get_dst(chan->conn), addr, sizeof(addr));
61
62 LOG_INF("Received SDU %d / %d from (%s)", tester->sdu_count, SDU_NUM, addr);
63
64 return 0;
65 }
66
l2cap_chan_connected_cb(struct bt_l2cap_chan * chan)67 static void l2cap_chan_connected_cb(struct bt_l2cap_chan *chan)
68 {
69 LOG_DBG("%p", chan);
70 }
71
l2cap_chan_disconnected_cb(struct bt_l2cap_chan * chan)72 static void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
73 {
74 LOG_DBG("%p", chan);
75 }
76
server_accept_cb(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)77 static int server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
78 struct bt_l2cap_chan **chan)
79 {
80 static struct bt_l2cap_chan_ops ops = {
81 .connected = l2cap_chan_connected_cb,
82 .disconnected = l2cap_chan_disconnected_cb,
83 .recv = recv_cb,
84 .sent = sent_cb,
85 };
86
87 struct tester *tester = get_tester(conn);
88 struct bt_l2cap_le_chan *le_chan = &tester->le_chan;
89
90 memset(le_chan, 0, sizeof(*le_chan));
91 le_chan->chan.ops = &ops;
92 *chan = &le_chan->chan;
93
94 return 0;
95 }
96
l2cap_server_register(bt_security_t sec_level)97 static int l2cap_server_register(bt_security_t sec_level)
98 {
99 static struct bt_l2cap_server test_l2cap_server = {.accept = server_accept_cb};
100
101 test_l2cap_server.psm = L2CAP_TEST_PSM;
102 test_l2cap_server.sec_level = sec_level;
103
104 int err = bt_l2cap_server_register(&test_l2cap_server);
105
106 TEST_ASSERT(err == 0, "Failed to register l2cap server (err %d)", err);
107
108 return test_l2cap_server.psm;
109 }
110
connect_tester(void)111 static struct bt_conn *connect_tester(void)
112 {
113 int err;
114 bt_addr_le_t tester = {};
115 struct bt_conn *conn = NULL;
116 char addr[BT_ADDR_LE_STR_LEN];
117
118 /* The device address will not change. Scan only once in order to reduce
119 * test time.
120 */
121 err = bt_testlib_scan_find_name(&tester, TESTER_NAME);
122 TEST_ASSERT(!err, "Failed to start scan (err %d)", err);
123
124 /* Create a connection using that address */
125 err = bt_testlib_connect(&tester, &conn);
126 TEST_ASSERT(!err, "Failed to initiate connection (err %d)", err);
127
128 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
129 LOG_DBG("Connected to %s", addr);
130
131 return conn;
132 }
133
all_data_transferred(void)134 static bool all_data_transferred(void)
135 {
136 size_t total_sdu_count = 0;
137
138 for (size_t i = 0; i < ARRAY_SIZE(testers); i++) {
139 total_sdu_count += testers[i].sdu_count;
140 }
141
142 TEST_ASSERT(total_sdu_count <= (SDU_NUM * NUM_TESTERS), "Received more SDUs than expected");
143
144 return total_sdu_count == (SDU_NUM * NUM_TESTERS);
145 }
146
entrypoint_dut(void)147 void entrypoint_dut(void)
148 {
149 /* Multilink Host Flow Control (HFC) test
150 *
151 * Test purpose:
152 *
153 * Verifies that we are able to do L2CAP recombination on multiple links
154 * when we have the smallest possible amount of ACL buffers.
155 *
156 * Devices:
157 * - `dut`: receives L2CAP PDUs from testers
158 * - `tester`: send ACL packets (parts of large L2CAP PDU) very slowly
159 *
160 * Procedure:
161 *
162 * DUT:
163 * - establish connection to tester
164 * - [acl connected]
165 * - establish L2CAP channel
166 * - [l2 connected]
167 * - receive L2CAP PDUs until SDU_NUM is reached
168 * - mark test as passed and terminate simulation
169 *
170 * tester 0/1/2:
171 * - scan & connect ACL
172 * - [acl connected]
173 * - [l2cap dynamic channel connected]
174 * (and then in a loop)
175 * - send part of L2CAP PDU
176 * - wait a set amount of time
177 * - exit loop when SDU_NUM sent
178 *
179 * [verdict]
180 * - dut application is able to receive all expected L2CAP packets from
181 * the testers
182 */
183 int err;
184
185 /* Mark test as in progress. */
186 TEST_START("dut");
187
188 /* Initialize Bluetooth */
189 err = bt_enable(NULL);
190 TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
191
192 LOG_DBG("Bluetooth initialized");
193
194 int psm = l2cap_server_register(BT_SECURITY_L1);
195
196 LOG_DBG("Registered server PSM %x", psm);
197
198 for (size_t i = 0; i < ARRAY_SIZE(testers); i++) {
199 LOG_DBG("Connecting tester %d", i);
200 testers[i].sdu_count = 0;
201 testers[i].conn = connect_tester();
202 }
203
204 LOG_DBG("Connected all testers");
205
206 while (!all_data_transferred()) {
207 /* Wait until we have received all expected data. */
208 k_sleep(K_MSEC(100));
209 }
210
211 TEST_PASS_AND_EXIT("dut");
212 }
213