1 /*
2  * Copyright (c) 2023 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "bstests.h"
8 #include "common.h"
9 
10 #define LOG_MODULE_NAME main
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG);
13 
14 CREATE_FLAG(is_connected);
15 CREATE_FLAG(flag_l2cap_connected);
16 
17 #define L2CAP_MPS CONFIG_BT_L2CAP_TX_MTU
18 #define SDU_NUM   3
19 #define SDU_LEN   (2 * L2CAP_MPS)
20 /* We intentionally send smaller SDUs than the channel can fit */
21 #define L2CAP_MTU (2 * SDU_LEN)
22 
23 /* Only one SDU transmitted or received at a time */
24 NET_BUF_POOL_DEFINE(sdu_pool, 1, BT_L2CAP_SDU_BUF_SIZE(L2CAP_MTU),
25 		    CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
26 
27 static uint8_t tx_data[SDU_LEN];
28 static uint16_t rx_cnt;
29 
30 K_SEM_DEFINE(sdu_received, 0, 1);
31 
32 struct test_ctx {
33 	struct bt_l2cap_le_chan le_chan;
34 	size_t tx_left;
35 } test_ctx;
36 
l2cap_chan_send(struct bt_l2cap_chan * chan,uint8_t * data,size_t len)37 int l2cap_chan_send(struct bt_l2cap_chan *chan, uint8_t *data, size_t len)
38 {
39 	struct net_buf *buf;
40 	int ret;
41 
42 	LOG_DBG("chan %p conn %u data %p len %d", chan, bt_conn_index(chan->conn), data, len);
43 
44 	buf = net_buf_alloc(&sdu_pool, K_NO_WAIT);
45 
46 	if (buf == NULL) {
47 		FAIL("No more memory\n");
48 		return -ENOMEM;
49 	}
50 
51 	net_buf_reserve(buf, BT_L2CAP_SDU_CHAN_SEND_RESERVE);
52 	net_buf_add_mem(buf, data, len);
53 
54 	ret = bt_l2cap_chan_send(chan, buf);
55 
56 	ASSERT(ret >= 0, "Failed sending: err %d", ret);
57 
58 	LOG_DBG("sent %d len %d", ret, len);
59 	return ret;
60 }
61 
continue_sending(struct test_ctx * ctx)62 void continue_sending(struct test_ctx *ctx)
63 {
64 	struct bt_l2cap_chan *chan = &ctx->le_chan.chan;
65 
66 	LOG_DBG("%p, left %d", chan, ctx->tx_left);
67 
68 	if (ctx->tx_left) {
69 		l2cap_chan_send(chan, tx_data, sizeof(tx_data));
70 	} else {
71 		LOG_DBG("Done sending %u", bt_conn_index(chan->conn));
72 	}
73 }
74 
sent_cb(struct bt_l2cap_chan * chan)75 void sent_cb(struct bt_l2cap_chan *chan)
76 {
77 	LOG_DBG("%p", chan);
78 
79 	if (test_ctx.tx_left) {
80 		test_ctx.tx_left--;
81 	}
82 
83 	continue_sending(&test_ctx);
84 }
85 
recv_cb(struct bt_l2cap_chan * l2cap_chan,size_t sdu_len,off_t seg_offset,struct net_buf_simple * seg)86 void recv_cb(struct bt_l2cap_chan *l2cap_chan, size_t sdu_len, off_t seg_offset,
87 	     struct net_buf_simple *seg)
88 {
89 	LOG_DBG("sdu len %u frag offset %u frag len %u", sdu_len, seg_offset, seg->len);
90 
91 	ASSERT(sdu_len == sizeof(tx_data), "Recv SDU length does not match send length.");
92 
93 	/* Verify SDU data matches TX'd data. */
94 	ASSERT(memcmp(seg->data, &tx_data[seg_offset], seg->len) == 0, "RX data doesn't match TX");
95 
96 	if (seg_offset + seg->len == sdu_len) {
97 		/* Don't give credits right away. The taker of this
98 		 * semaphore will give the credits after sleeping a bit.
99 		 */
100 		rx_cnt++;
101 		k_sem_give(&sdu_received);
102 	} else {
103 		/* To test that we really gave two initial credits, we
104 		 * "forget" to send one credit after the first PDU of
105 		 * the first SDU.
106 		 */
107 		if (!(rx_cnt == 0 && seg_offset == 0)) {
108 			/* Give more credits to complete SDU. */
109 			LOG_DBG("Giving credits for continuing SDU.");
110 			bt_l2cap_chan_give_credits(&test_ctx.le_chan.chan, 1);
111 		}
112 	}
113 }
114 
l2cap_chan_connected_cb(struct bt_l2cap_chan * l2cap_chan)115 void l2cap_chan_connected_cb(struct bt_l2cap_chan *l2cap_chan)
116 {
117 	struct bt_l2cap_le_chan *chan = CONTAINER_OF(l2cap_chan, struct bt_l2cap_le_chan, chan);
118 
119 	/* TODO: check that actual MPS < expected MPS */
120 
121 	SET_FLAG(flag_l2cap_connected);
122 	LOG_DBG("%x (tx mtu %d mps %d) (tx mtu %d mps %d)", l2cap_chan, chan->tx.mtu, chan->tx.mps,
123 		chan->rx.mtu, chan->rx.mps);
124 }
125 
l2cap_chan_disconnected_cb(struct bt_l2cap_chan * chan)126 void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
127 {
128 	UNSET_FLAG(flag_l2cap_connected);
129 	LOG_DBG("%p", chan);
130 }
131 
132 static struct bt_l2cap_chan_ops ops = {
133 	.connected = l2cap_chan_connected_cb,
134 	.disconnected = l2cap_chan_disconnected_cb,
135 	.seg_recv = recv_cb,
136 	.sent = sent_cb,
137 };
138 
server_accept_cb(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)139 int server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
140 		     struct bt_l2cap_chan **chan)
141 {
142 	struct bt_l2cap_le_chan *le_chan = &test_ctx.le_chan;
143 
144 	*chan = &le_chan->chan;
145 
146 	/* Always default initialize the chan. */
147 	memset(le_chan, 0, sizeof(*le_chan));
148 	le_chan->chan.ops = &ops;
149 
150 	le_chan->rx.mtu = L2CAP_MTU;
151 	le_chan->rx.mps = BT_L2CAP_RX_MTU;
152 
153 	/* Credits can be given before returning from this
154 	 * accept-handler and after the 'connected' event. Credits given
155 	 * before completing the accept are sent in the 'initial
156 	 * credits' field of the connection response PDU.
157 	 */
158 	bt_l2cap_chan_give_credits(*chan, 2);
159 
160 	return 0;
161 }
162 
163 static struct bt_l2cap_server test_l2cap_server = {.accept = server_accept_cb};
164 
l2cap_server_register(bt_security_t sec_level)165 static int l2cap_server_register(bt_security_t sec_level)
166 {
167 	int err;
168 
169 	test_l2cap_server.psm = 0;
170 	test_l2cap_server.sec_level = sec_level;
171 
172 	err = bt_l2cap_server_register(&test_l2cap_server);
173 
174 	ASSERT(err == 0, "Failed to register l2cap server.");
175 
176 	return test_l2cap_server.psm;
177 }
178 
connected(struct bt_conn * conn,uint8_t conn_err)179 static void connected(struct bt_conn *conn, uint8_t conn_err)
180 {
181 	char addr[BT_ADDR_LE_STR_LEN];
182 
183 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
184 
185 	if (conn_err) {
186 		FAIL("Failed to connect to %s (%u)", addr, conn_err);
187 		return;
188 	}
189 
190 	LOG_DBG("%s", addr);
191 
192 	SET_FLAG(is_connected);
193 }
194 
disconnected(struct bt_conn * conn,uint8_t reason)195 static void disconnected(struct bt_conn *conn, uint8_t reason)
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 	LOG_DBG("%p %s (reason 0x%02x)", conn, addr, reason);
202 
203 	UNSET_FLAG(is_connected);
204 }
205 
206 BT_CONN_CB_DEFINE(conn_callbacks) = {
207 	.connected = connected,
208 	.disconnected = disconnected,
209 };
210 
disconnect_device(struct bt_conn * conn,void * data)211 static void disconnect_device(struct bt_conn *conn, void *data)
212 {
213 	int err;
214 
215 	SET_FLAG(is_connected);
216 
217 	err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
218 	ASSERT(!err, "Failed to initate disconnect (err %d)", err);
219 
220 	LOG_DBG("Waiting for disconnection...");
221 	WAIT_FOR_FLAG_UNSET(is_connected);
222 }
223 
test_peripheral_main(void)224 static void test_peripheral_main(void)
225 {
226 	int err;
227 	int psm;
228 
229 	LOG_DBG("*L2CAP CREDITS Peripheral started*");
230 
231 	/* Prepare tx_data */
232 	for (size_t i = 0; i < sizeof(tx_data); i++) {
233 		tx_data[i] = (uint8_t)i;
234 	}
235 
236 	err = bt_enable(NULL);
237 	if (err) {
238 		FAIL("Can't enable Bluetooth (err %d)", err);
239 		return;
240 	}
241 
242 	LOG_DBG("Peripheral Bluetooth initialized.");
243 	LOG_DBG("Connectable advertising...");
244 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, NULL, 0, NULL, 0);
245 	if (err) {
246 		FAIL("Advertising failed to start (err %d)", err);
247 		return;
248 	}
249 
250 	LOG_DBG("Advertising started.");
251 	LOG_DBG("Peripheral waiting for connection...");
252 	WAIT_FOR_FLAG_SET(is_connected);
253 	LOG_DBG("Peripheral Connected.");
254 
255 	psm = l2cap_server_register(BT_SECURITY_L1);
256 
257 	LOG_DBG("Registered server PSM %x", psm);
258 
259 	LOG_DBG("Peripheral waiting for transfer completion");
260 	while (rx_cnt < SDU_NUM) {
261 		k_sem_take(&sdu_received, K_FOREVER);
262 
263 		/* Sleep enough so the peer has time to attempt sending another
264 		 * SDU. If it still has credits, it's in its right to do so. If
265 		 * it does so before we release the ref below, then allocation
266 		 * will fail and the channel will be disconnected.
267 		 */
268 		k_sleep(K_SECONDS(5));
269 		LOG_DBG("release SDU ref");
270 		LOG_DBG("Giving credits for new SDU.");
271 		bt_l2cap_chan_give_credits(&test_ctx.le_chan.chan, 1);
272 	}
273 
274 	bt_conn_foreach(BT_CONN_TYPE_LE, disconnect_device, NULL);
275 	LOG_INF("Total received: %d", rx_cnt);
276 
277 	ASSERT(rx_cnt == SDU_NUM, "Did not receive expected no of SDUs\n");
278 
279 	PASS("L2CAP CREDITS Peripheral passed\n");
280 }
281 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)282 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
283 			 struct net_buf_simple *ad)
284 {
285 	char str[BT_ADDR_LE_STR_LEN];
286 	int err;
287 	struct bt_conn *conn;
288 	struct bt_le_conn_param *param;
289 
290 	err = bt_le_scan_stop();
291 	if (err) {
292 		FAIL("Stop LE scan failed (err %d)", err);
293 		return;
294 	}
295 
296 	bt_addr_le_to_str(addr, str, sizeof(str));
297 
298 	LOG_DBG("Connecting to %s", str);
299 
300 	param = BT_LE_CONN_PARAM_DEFAULT;
301 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &conn);
302 	if (err) {
303 		FAIL("Create conn failed (err %d)", err);
304 		return;
305 	}
306 }
307 
connect_peripheral(void)308 static void connect_peripheral(void)
309 {
310 	struct bt_le_scan_param scan_param = {
311 		.type = BT_LE_SCAN_TYPE_ACTIVE,
312 		.options = BT_LE_SCAN_OPT_NONE,
313 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
314 		.window = BT_GAP_SCAN_FAST_WINDOW,
315 	};
316 	int err;
317 
318 	UNSET_FLAG(is_connected);
319 
320 	err = bt_le_scan_start(&scan_param, device_found);
321 
322 	ASSERT(!err, "Scanning failed to start (err %d)\n", err);
323 
324 	LOG_DBG("Central initiating connection...");
325 	WAIT_FOR_FLAG_SET(is_connected);
326 }
327 
connect_l2cap_channel(struct bt_conn * conn,void * data)328 static void connect_l2cap_channel(struct bt_conn *conn, void *data)
329 {
330 	int err;
331 	struct bt_l2cap_le_chan *le_chan = &test_ctx.le_chan;
332 
333 	*le_chan = (struct bt_l2cap_le_chan){
334 		.chan.ops = &ops,
335 		.rx.mtu = L2CAP_MTU,
336 		.rx.mps = BT_L2CAP_RX_MTU,
337 	};
338 
339 	UNSET_FLAG(flag_l2cap_connected);
340 
341 	/* Credits can be given before requesting the connection and
342 	 * after the 'connected' event. Credits given before connecting
343 	 * are sent in the 'initial credits' field of the connection
344 	 * request PDU.
345 	 */
346 	bt_l2cap_chan_give_credits(&le_chan->chan, 1);
347 
348 	err = bt_l2cap_chan_connect(conn, &le_chan->chan, 0x0080);
349 	ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
350 
351 	WAIT_FOR_FLAG_SET(flag_l2cap_connected);
352 }
353 
connect_l2cap_ecred_channel(struct bt_conn * conn,void * data)354 static void connect_l2cap_ecred_channel(struct bt_conn *conn, void *data)
355 {
356 	int err;
357 	struct bt_l2cap_le_chan *le_chan = &test_ctx.le_chan;
358 	struct bt_l2cap_chan *chan_list[2] = {&le_chan->chan, 0};
359 
360 	*le_chan = (struct bt_l2cap_le_chan){
361 		.chan.ops = &ops,
362 		.rx.mtu = L2CAP_MTU,
363 		.rx.mps = BT_L2CAP_RX_MTU,
364 	};
365 
366 	UNSET_FLAG(flag_l2cap_connected);
367 
368 	/* Credits can be given before requesting the connection and
369 	 * after the 'connected' event. Credits given before connecting
370 	 * are sent in the 'initial credits' field of the connection
371 	 * request PDU.
372 	 */
373 	bt_l2cap_chan_give_credits(&le_chan->chan, 1);
374 
375 	err = bt_l2cap_ecred_chan_connect(conn, chan_list, 0x0080);
376 	ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
377 
378 	WAIT_FOR_FLAG_SET(flag_l2cap_connected);
379 }
380 
test_central_main(void)381 static void test_central_main(void)
382 {
383 	int err;
384 
385 	LOG_DBG("*L2CAP CREDITS Central started*");
386 
387 	/* Prepare tx_data */
388 	for (size_t i = 0; i < sizeof(tx_data); i++) {
389 		tx_data[i] = (uint8_t)i;
390 	}
391 
392 	err = bt_enable(NULL);
393 	ASSERT(err == 0, "Can't enable Bluetooth (err %d)\n", err);
394 	LOG_DBG("Central Bluetooth initialized.");
395 
396 	connect_peripheral();
397 
398 	/* Connect L2CAP channels */
399 	LOG_DBG("Connect L2CAP channels");
400 	if (IS_ENABLED(CONFIG_BT_L2CAP_ECRED)) {
401 		bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_ecred_channel, NULL);
402 	} else {
403 		bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_channel, NULL);
404 	}
405 
406 	/* Send SDU_NUM SDUs to each peripheral */
407 	test_ctx.tx_left = SDU_NUM;
408 	l2cap_chan_send(&test_ctx.le_chan.chan, tx_data, sizeof(tx_data));
409 
410 	LOG_DBG("Wait until all transfers are completed.");
411 	while (test_ctx.tx_left) {
412 		k_msleep(100);
413 	}
414 
415 	WAIT_FOR_FLAG_UNSET(is_connected);
416 	LOG_DBG("Peripheral disconnected.");
417 	PASS("L2CAP CREDITS Central passed\n");
418 }
419 
420 static const struct bst_test_instance test_def[] = {
421 	{.test_id = "peripheral",
422 	 .test_descr = "Peripheral L2CAP CREDITS",
423 	 .test_pre_init_f = test_init,
424 	 .test_tick_f = test_tick,
425 	 .test_main_f = test_peripheral_main},
426 	{.test_id = "central",
427 	 .test_descr = "Central L2CAP CREDITS",
428 	 .test_pre_init_f = test_init,
429 	 .test_tick_f = test_tick,
430 	 .test_main_f = test_central_main},
431 	BSTEST_END_MARKER,
432 };
433 
test_main_l2cap_credits_install(struct bst_test_list * tests)434 struct bst_test_list *test_main_l2cap_credits_install(struct bst_test_list *tests)
435 {
436 	return bst_add_tests(tests, test_def);
437 }
438 
439 extern struct bst_test_list *test_main_l2cap_credits_install(struct bst_test_list *tests);
440 
441 bst_test_install_t test_installers[] = {test_main_l2cap_credits_install, NULL};
442 
main(void)443 int main(void)
444 {
445 	bst_main();
446 
447 	return 0;
448 }
449