1 /* Application main entry point */
2 
3 /*
4  * Copyright (c) 2024 Nordic Semiconductor
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include "bstests.h"
10 #include "common.h"
11 
12 #define LOG_MODULE_NAME main
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF);
15 
16 CREATE_FLAG(is_connected);
17 CREATE_FLAG(flag_l2cap_connected);
18 
19 #define NUM_PERIPHERALS CONFIG_BT_MAX_CONN
20 #define L2CAP_CHANS     NUM_PERIPHERALS
21 #define SDU_NUM         1
22 #define SDU_LEN         10
23 
24 /* Only one SDU per link will be transmitted */
25 NET_BUF_POOL_DEFINE(sdu_tx_pool,
26 		    CONFIG_BT_MAX_CONN, BT_L2CAP_SDU_BUF_SIZE(SDU_LEN),
27 		    CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
28 
29 /* Only one SDU per link will be received at a time */
30 NET_BUF_POOL_DEFINE(sdu_rx_pool,
31 		    CONFIG_BT_MAX_CONN, BT_L2CAP_SDU_BUF_SIZE(SDU_LEN),
32 		    CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
33 
34 static uint8_t tx_data[SDU_LEN];
35 static uint16_t rx_cnt;
36 static uint8_t disconnect_counter;
37 
38 struct test_ctx {
39 	struct bt_l2cap_le_chan le_chan;
40 	size_t tx_left;
41 };
42 
43 static struct test_ctx contexts[L2CAP_CHANS];
44 
get_ctx(struct bt_l2cap_chan * chan)45 struct test_ctx *get_ctx(struct bt_l2cap_chan *chan)
46 {
47 	struct bt_l2cap_le_chan *le_chan = CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan);
48 	struct test_ctx *ctx = CONTAINER_OF(le_chan, struct test_ctx, le_chan);
49 
50 	ASSERT(ctx >= &contexts[0] &&
51 	       ctx <= &contexts[L2CAP_CHANS], "memory corruption");
52 
53 	return ctx;
54 }
55 
l2cap_chan_send(struct bt_l2cap_chan * chan,uint8_t * data,size_t len)56 int l2cap_chan_send(struct bt_l2cap_chan *chan, uint8_t *data, size_t len)
57 {
58 	LOG_DBG("chan %p conn %u data %p len %d", chan, bt_conn_index(chan->conn), data, len);
59 
60 	struct net_buf *buf = net_buf_alloc(&sdu_tx_pool, K_NO_WAIT);
61 
62 	ASSERT(buf, "No more memory\n");
63 
64 	net_buf_reserve(buf, BT_L2CAP_SDU_CHAN_SEND_RESERVE);
65 	net_buf_add_mem(buf, data, len);
66 
67 	int ret = bt_l2cap_chan_send(chan, buf);
68 
69 	ASSERT(ret >= 0, "Failed sending: err %d", ret);
70 
71 	LOG_DBG("sent %d len %d", ret, len);
72 	return ret;
73 }
74 
alloc_buf_cb(struct bt_l2cap_chan * chan)75 struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
76 {
77 	return net_buf_alloc(&sdu_rx_pool, K_NO_WAIT);
78 }
79 
sent_cb(struct bt_l2cap_chan * chan)80 void sent_cb(struct bt_l2cap_chan *chan)
81 {
82 	struct test_ctx *ctx = get_ctx(chan);
83 
84 	LOG_DBG("%p", chan);
85 
86 	if (ctx->tx_left) {
87 		ctx->tx_left--;
88 	}
89 }
90 
recv_cb(struct bt_l2cap_chan * chan,struct net_buf * buf)91 int recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
92 {
93 	LOG_DBG("len %d", buf->len);
94 	rx_cnt++;
95 
96 	/* Verify SDU data matches TX'd data. */
97 	int pos = memcmp(buf->data, tx_data, buf->len);
98 
99 	if (pos != 0) {
100 		LOG_ERR("RX data doesn't match TX: pos %d", pos);
101 		LOG_HEXDUMP_ERR(buf->data, buf->len, "RX data");
102 		LOG_HEXDUMP_INF(tx_data, buf->len, "TX data");
103 
104 		for (uint16_t p = 0; p < buf->len; p++) {
105 			__ASSERT(buf->data[p] == tx_data[p],
106 				 "Failed rx[%d]=%x != expect[%d]=%x",
107 				 p, buf->data[p], p, tx_data[p]);
108 		}
109 	}
110 
111 	return 0;
112 }
113 
l2cap_chan_connected_cb(struct bt_l2cap_chan * l2cap_chan)114 void l2cap_chan_connected_cb(struct bt_l2cap_chan *l2cap_chan)
115 {
116 	struct bt_l2cap_le_chan *chan =
117 		CONTAINER_OF(l2cap_chan, struct bt_l2cap_le_chan, chan);
118 
119 	SET_FLAG(flag_l2cap_connected);
120 	LOG_DBG("%x (tx mtu %d mps %d) (tx mtu %d mps %d)",
121 		l2cap_chan,
122 		chan->tx.mtu,
123 		chan->tx.mps,
124 		chan->rx.mtu,
125 		chan->rx.mps);
126 }
127 
l2cap_chan_disconnected_cb(struct bt_l2cap_chan * chan)128 void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
129 {
130 	UNSET_FLAG(flag_l2cap_connected);
131 	LOG_DBG("%p", chan);
132 }
133 
134 static struct bt_l2cap_chan_ops ops = {
135 	.connected = l2cap_chan_connected_cb,
136 	.disconnected = l2cap_chan_disconnected_cb,
137 	.alloc_buf = alloc_buf_cb,
138 	.recv = recv_cb,
139 	.sent = sent_cb,
140 };
141 
alloc_test_context(void)142 struct test_ctx *alloc_test_context(void)
143 {
144 	for (int i = 0; i < L2CAP_CHANS; i++) {
145 		struct bt_l2cap_le_chan *le_chan = &contexts[i].le_chan;
146 
147 		if (le_chan->state != BT_L2CAP_DISCONNECTED) {
148 			continue;
149 		}
150 
151 		memset(&contexts[i], 0, sizeof(struct test_ctx));
152 
153 		return &contexts[i];
154 	}
155 
156 	return NULL;
157 }
158 
server_accept_cb(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)159 int server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
160 		     struct bt_l2cap_chan **chan)
161 {
162 	struct test_ctx *ctx = NULL;
163 
164 	ctx = alloc_test_context();
165 	if (ctx == NULL) {
166 		return -ENOMEM;
167 	}
168 
169 	struct bt_l2cap_le_chan *le_chan = &ctx->le_chan;
170 
171 	memset(le_chan, 0, sizeof(*le_chan));
172 	le_chan->chan.ops = &ops;
173 	le_chan->rx.mtu = SDU_LEN;
174 	*chan = &le_chan->chan;
175 
176 	return 0;
177 }
178 
179 static struct bt_l2cap_server test_l2cap_server = {
180 	.accept = server_accept_cb
181 };
182 
l2cap_server_register(bt_security_t sec_level)183 static int l2cap_server_register(bt_security_t sec_level)
184 {
185 	test_l2cap_server.psm = 0;
186 	test_l2cap_server.sec_level = sec_level;
187 
188 	int err = bt_l2cap_server_register(&test_l2cap_server);
189 
190 	ASSERT(err == 0, "Failed to register l2cap server.");
191 
192 	return test_l2cap_server.psm;
193 }
194 
connected(struct bt_conn * conn,uint8_t conn_err)195 static void connected(struct bt_conn *conn, uint8_t conn_err)
196 {
197 	char addr[BT_ADDR_LE_STR_LEN];
198 
199 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
200 
201 	ASSERT(!conn_err, "Failed to connect to %s (%u)", addr, conn_err);
202 
203 	LOG_DBG("%s", addr);
204 
205 	SET_FLAG(is_connected);
206 }
207 
disconnected(struct bt_conn * conn,uint8_t reason)208 static void disconnected(struct bt_conn *conn, uint8_t reason)
209 {
210 	char addr[BT_ADDR_LE_STR_LEN];
211 
212 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
213 
214 	LOG_DBG("%p %s (reason 0x%02x)", conn, addr, reason);
215 
216 	UNSET_FLAG(is_connected);
217 	disconnect_counter++;
218 }
219 
220 BT_CONN_CB_DEFINE(conn_callbacks) = {
221 	.connected = connected,
222 	.disconnected = disconnected,
223 };
224 
disconnect_device(struct bt_conn * conn,void * data)225 static void disconnect_device(struct bt_conn *conn, void *data)
226 {
227 	int err;
228 
229 	SET_FLAG(is_connected);
230 
231 	err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
232 	ASSERT(!err, "Failed to initate disconnect (err %d)", err);
233 
234 	LOG_DBG("Waiting for disconnection...");
235 	WAIT_FOR_FLAG_UNSET(is_connected);
236 }
237 
test_peripheral_main(void)238 static void test_peripheral_main(void)
239 {
240 	LOG_DBG("L2CAP CONN LATENCY Peripheral started*");
241 	int err;
242 
243 	/* Prepare tx_data */
244 	for (size_t i = 0; i < sizeof(tx_data); i++) {
245 		tx_data[i] = (uint8_t)i;
246 	}
247 
248 	err = bt_enable(NULL);
249 	ASSERT(!err, "Can't enable Bluetooth (err %d)", err);
250 
251 	LOG_DBG("Peripheral Bluetooth initialized.");
252 	LOG_DBG("Connectable advertising...");
253 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, NULL, 0, NULL, 0);
254 	ASSERT(!err, "Advertising failed to start (err %d)", err);
255 
256 	LOG_DBG("Advertising started.");
257 	LOG_DBG("Peripheral waiting for connection...");
258 	WAIT_FOR_FLAG_SET(is_connected);
259 	LOG_DBG("Peripheral Connected.");
260 
261 	int psm = l2cap_server_register(BT_SECURITY_L1);
262 
263 	LOG_DBG("Registered server PSM %x", psm);
264 
265 	LOG_DBG("Peripheral waiting for transfer completion");
266 	while (rx_cnt < SDU_NUM) {
267 		k_msleep(100);
268 	}
269 
270 	bt_conn_foreach(BT_CONN_TYPE_LE, disconnect_device, NULL);
271 	WAIT_FOR_FLAG_UNSET(is_connected);
272 	LOG_INF("Total received: %d", rx_cnt);
273 
274 	ASSERT(rx_cnt == SDU_NUM, "Did not receive expected no of SDUs\n");
275 
276 	PASS("L2CAP LATENCY Peripheral passed\n");
277 }
278 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)279 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
280 			 struct net_buf_simple *ad)
281 {
282 	struct bt_le_conn_param *param;
283 	struct bt_conn *conn;
284 	int err;
285 
286 	err = bt_le_scan_stop();
287 	ASSERT(!err, "Stop LE scan failed (err %d)", err);
288 
289 	char str[BT_ADDR_LE_STR_LEN];
290 
291 	bt_addr_le_to_str(addr, str, sizeof(str));
292 
293 	LOG_DBG("Connecting to %s", str);
294 
295 	param = BT_LE_CONN_PARAM_DEFAULT;
296 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &conn);
297 	ASSERT(!err, "Create conn failed (err %d)", err);
298 }
299 
connect_peripheral(void)300 static void connect_peripheral(void)
301 {
302 	struct bt_le_scan_param scan_param = {
303 		.type = BT_LE_SCAN_TYPE_ACTIVE,
304 		.options = BT_LE_SCAN_OPT_NONE,
305 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
306 		.window = BT_GAP_SCAN_FAST_WINDOW,
307 	};
308 
309 	UNSET_FLAG(is_connected);
310 
311 	int err = bt_le_scan_start(&scan_param, device_found);
312 
313 	ASSERT(!err, "Scanning failed to start (err %d)\n", err);
314 
315 	LOG_DBG("Central initiating connection...");
316 	WAIT_FOR_FLAG_SET(is_connected);
317 }
318 
connect_l2cap_channel(struct bt_conn * conn,void * data)319 static void connect_l2cap_channel(struct bt_conn *conn, void *data)
320 {
321 	int err;
322 	struct test_ctx *ctx = alloc_test_context();
323 
324 	ASSERT(ctx, "No more available test contexts\n");
325 
326 	struct bt_l2cap_le_chan *le_chan = &ctx->le_chan;
327 
328 	le_chan->chan.ops = &ops;
329 
330 	UNSET_FLAG(flag_l2cap_connected);
331 
332 	err = bt_l2cap_chan_connect(conn, &le_chan->chan, 0x0080);
333 	ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
334 
335 	WAIT_FOR_FLAG_SET(flag_l2cap_connected);
336 }
337 
338 #define L2CAP_LE_CID_DYN_START	0x0040
339 #define L2CAP_LE_CID_DYN_END	0x007f
340 #define L2CAP_LE_CID_IS_DYN(_cid) \
341 	(_cid >= L2CAP_LE_CID_DYN_START && _cid <= L2CAP_LE_CID_DYN_END)
342 
is_dynamic(struct bt_l2cap_le_chan * lechan)343 static bool is_dynamic(struct bt_l2cap_le_chan *lechan)
344 {
345 	return L2CAP_LE_CID_IS_DYN(lechan->tx.cid);
346 }
347 
bt_test_l2cap_data_pull_spy(struct bt_conn * conn,struct bt_l2cap_le_chan * lechan,size_t amount,size_t * length)348 void bt_test_l2cap_data_pull_spy(struct bt_conn *conn,
349 				 struct bt_l2cap_le_chan *lechan,
350 				 size_t amount,
351 				 size_t *length)
352 {
353 	static uint32_t last_pull_time;
354 	uint32_t uptime = k_uptime_get_32();
355 
356 	if (lechan == NULL || !is_dynamic(lechan)) {
357 		/* only interested in application-generated data */
358 		return;
359 	}
360 
361 	if (last_pull_time == 0) {
362 		last_pull_time = uptime;
363 		return;
364 	}
365 
366 	ASSERT(uptime == last_pull_time,
367 	       "Too much delay servicing ready channels\n");
368 }
369 
test_central_main(void)370 static void test_central_main(void)
371 {
372 	LOG_DBG("L2CAP CONN LATENCY Central started*");
373 	int err;
374 
375 	/* Prepare tx_data */
376 	for (size_t i = 0; i < sizeof(tx_data); i++) {
377 		tx_data[i] = (uint8_t)i;
378 	}
379 
380 	err = bt_enable(NULL);
381 	ASSERT(err == 0, "Can't enable Bluetooth (err %d)\n", err);
382 	LOG_DBG("Central Bluetooth initialized.");
383 
384 	/* Connect all peripherals */
385 	for (int i = 0; i < NUM_PERIPHERALS; i++) {
386 		connect_peripheral();
387 	}
388 
389 	/* Connect L2CAP channels */
390 	LOG_DBG("Connect L2CAP channels");
391 	bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_channel, NULL);
392 
393 	/* This is to allow the main thread to put PDUs for all the connections
394 	 * in the TX queues. This way we verify that we service as many
395 	 * connections as we have resources for.
396 	 */
397 	k_thread_priority_set(k_current_get(), K_HIGHEST_APPLICATION_THREAD_PRIO);
398 
399 	/* Send SDU_NUM SDUs to each peripheral */
400 	for (int i = 0; i < NUM_PERIPHERALS; i++) {
401 		contexts[i].tx_left = SDU_NUM;
402 		l2cap_chan_send(&contexts[i].le_chan.chan, tx_data, sizeof(tx_data));
403 	}
404 
405 	k_thread_priority_set(k_current_get(), CONFIG_MAIN_THREAD_PRIORITY);
406 
407 	LOG_DBG("Wait until all transfers are completed.");
408 	int remaining_tx_total;
409 
410 	/* Assertion that the `pull` callback gets serviced for all connections
411 	 * at the same time is handled in bt_l2cap_data_pull_spy().
412 	 */
413 
414 	do {
415 		k_msleep(100);
416 
417 		remaining_tx_total = 0;
418 		for (int i = 0; i < L2CAP_CHANS; i++) {
419 			remaining_tx_total += contexts[i].tx_left;
420 		}
421 	} while (remaining_tx_total);
422 
423 	LOG_DBG("Waiting until all peripherals are disconnected..");
424 	while (disconnect_counter < NUM_PERIPHERALS) {
425 		k_msleep(100);
426 	}
427 	LOG_DBG("All peripherals disconnected.");
428 
429 	PASS("L2CAP LATENCY Central passed\n");
430 }
431 
432 static const struct bst_test_instance test_def[] = {
433 	{
434 		.test_id = "peripheral",
435 		.test_descr = "Peripheral L2CAP LATENCY",
436 		.test_pre_init_f = test_init,
437 		.test_tick_f = test_tick,
438 		.test_main_f = test_peripheral_main
439 	},
440 	{
441 		.test_id = "central",
442 		.test_descr = "Central L2CAP LATENCY",
443 		.test_pre_init_f = test_init,
444 		.test_tick_f = test_tick,
445 		.test_main_f = test_central_main
446 	},
447 	BSTEST_END_MARKER
448 };
449 
test_main_l2cap_stress_install(struct bst_test_list * tests)450 struct bst_test_list *test_main_l2cap_stress_install(struct bst_test_list *tests)
451 {
452 	return bst_add_tests(tests, test_def);
453 }
454 
455 extern struct bst_test_list *test_main_l2cap_stress_install(struct bst_test_list *tests);
456 
457 bst_test_install_t test_installers[] = {
458 	test_main_l2cap_stress_install,
459 	NULL
460 };
461 
main(void)462 int main(void)
463 {
464 	bst_main();
465 	return 0;
466 }
467