1 /* main_l2cap_credits.c - Application main entry point */
2 
3 /*
4  * Copyright (c) 2022 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_DBG);
15 
16 CREATE_FLAG(is_connected);
17 CREATE_FLAG(flag_l2cap_connected);
18 
19 #define L2CAP_MPS       CONFIG_BT_L2CAP_TX_MTU
20 #define SDU_NUM         3
21 #define SDU_LEN         (2 * L2CAP_MPS)
22 /* We intentionally send smaller SDUs than the channel can fit */
23 #define L2CAP_MTU       (2 * SDU_LEN)
24 
25 /* Only one SDU transmitted or received at a time */
26 NET_BUF_POOL_DEFINE(sdu_pool, 1, BT_L2CAP_SDU_BUF_SIZE(L2CAP_MTU),
27 		    CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
28 
29 static uint8_t tx_data[SDU_LEN];
30 static uint16_t rx_cnt;
31 
32 K_SEM_DEFINE(sdu_received, 0, 1);
33 
34 struct test_ctx {
35 	struct bt_l2cap_le_chan le_chan;
36 	size_t tx_left;
37 	struct net_buf *rx_sdu;
38 } test_ctx;
39 
l2cap_chan_send(struct bt_l2cap_chan * chan,uint8_t * data,size_t len)40 int l2cap_chan_send(struct bt_l2cap_chan *chan, uint8_t *data, size_t len)
41 {
42 	LOG_DBG("chan %p conn %u data %p len %d", chan, bt_conn_index(chan->conn), data, len);
43 
44 	struct net_buf *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 	int 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 
alloc_buf_cb(struct bt_l2cap_chan * chan)62 struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
63 {
64 	return net_buf_alloc(&sdu_pool, K_NO_WAIT);
65 }
66 
continue_sending(struct test_ctx * ctx)67 void continue_sending(struct test_ctx *ctx)
68 {
69 	struct bt_l2cap_chan *chan = &ctx->le_chan.chan;
70 
71 	LOG_DBG("%p, left %d", chan, ctx->tx_left);
72 
73 	if (ctx->tx_left) {
74 		l2cap_chan_send(chan, tx_data, sizeof(tx_data));
75 	} else {
76 		LOG_DBG("Done sending %u", bt_conn_index(chan->conn));
77 	}
78 }
79 
sent_cb(struct bt_l2cap_chan * chan)80 void sent_cb(struct bt_l2cap_chan *chan)
81 {
82 	LOG_DBG("%p", chan);
83 
84 	if (test_ctx.tx_left) {
85 		test_ctx.tx_left--;
86 	}
87 
88 	continue_sending(&test_ctx);
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 	ASSERT(memcmp(buf->data, tx_data, buf->len) == 0, "RX data doesn't match TX");
98 
99 	/* Keep a ref for a few seconds: this will make the allocation fail, as
100 	 * there is only 1 buffer in the pool.
101 	 */
102 	LOG_DBG("take SDU ref");
103 	test_ctx.rx_sdu = net_buf_ref(buf);
104 	k_sem_give(&sdu_received);
105 
106 	return -EINPROGRESS;
107 }
108 
l2cap_chan_connected_cb(struct bt_l2cap_chan * l2cap_chan)109 void l2cap_chan_connected_cb(struct bt_l2cap_chan *l2cap_chan)
110 {
111 	struct bt_l2cap_le_chan *chan =
112 		CONTAINER_OF(l2cap_chan, struct bt_l2cap_le_chan, chan);
113 
114 	/* TODO: check that actual MPS < expected MPS */
115 
116 	SET_FLAG(flag_l2cap_connected);
117 	LOG_DBG("%x (tx mtu %d mps %d) (tx mtu %d mps %d)",
118 		l2cap_chan,
119 		chan->tx.mtu,
120 		chan->tx.mps,
121 		chan->rx.mtu,
122 		chan->rx.mps);
123 }
124 
l2cap_chan_disconnected_cb(struct bt_l2cap_chan * chan)125 void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
126 {
127 	UNSET_FLAG(flag_l2cap_connected);
128 	LOG_DBG("%p", chan);
129 }
130 
131 static struct bt_l2cap_chan_ops ops = {
132 	.connected = l2cap_chan_connected_cb,
133 	.disconnected = l2cap_chan_disconnected_cb,
134 	.alloc_buf = alloc_buf_cb,
135 	.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 	memset(le_chan, 0, sizeof(*le_chan));
145 	le_chan->chan.ops = &ops;
146 	le_chan->rx.mtu = L2CAP_MTU;
147 	*chan = &le_chan->chan;
148 
149 	return 0;
150 }
151 
152 static struct bt_l2cap_server test_l2cap_server = {
153 	.accept = server_accept_cb
154 };
155 
l2cap_server_register(bt_security_t sec_level)156 static int l2cap_server_register(bt_security_t sec_level)
157 {
158 	test_l2cap_server.psm = 0;
159 	test_l2cap_server.sec_level = sec_level;
160 
161 	int err = bt_l2cap_server_register(&test_l2cap_server);
162 
163 	ASSERT(err == 0, "Failed to register l2cap server.");
164 
165 	return test_l2cap_server.psm;
166 }
167 
connected(struct bt_conn * conn,uint8_t conn_err)168 static void connected(struct bt_conn *conn, uint8_t conn_err)
169 {
170 	char addr[BT_ADDR_LE_STR_LEN];
171 
172 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
173 
174 	if (conn_err) {
175 		FAIL("Failed to connect to %s (%u)", addr, conn_err);
176 		return;
177 	}
178 
179 	LOG_DBG("%s", addr);
180 
181 	SET_FLAG(is_connected);
182 }
183 
disconnected(struct bt_conn * conn,uint8_t reason)184 static void disconnected(struct bt_conn *conn, uint8_t reason)
185 {
186 	char addr[BT_ADDR_LE_STR_LEN];
187 
188 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
189 
190 	LOG_DBG("%p %s (reason 0x%02x)", conn, addr, reason);
191 
192 	UNSET_FLAG(is_connected);
193 }
194 
195 BT_CONN_CB_DEFINE(conn_callbacks) = {
196 	.connected = connected,
197 	.disconnected = disconnected,
198 };
199 
disconnect_device(struct bt_conn * conn,void * data)200 static void disconnect_device(struct bt_conn *conn, void *data)
201 {
202 	int err;
203 
204 	SET_FLAG(is_connected);
205 
206 	err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
207 	ASSERT(!err, "Failed to initate disconnect (err %d)", err);
208 
209 	LOG_DBG("Waiting for disconnection...");
210 	WAIT_FOR_FLAG_UNSET(is_connected);
211 }
212 
213 #define BT_LE_ADV_CONN_NAME_OT BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \
214 					    BT_LE_ADV_OPT_USE_NAME |	\
215 					    BT_LE_ADV_OPT_ONE_TIME,	\
216 					    BT_GAP_ADV_FAST_INT_MIN_2, \
217 					    BT_GAP_ADV_FAST_INT_MAX_2, NULL)
218 
219 static const struct bt_data ad[] = {
220 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
221 };
222 
test_peripheral_main(void)223 static void test_peripheral_main(void)
224 {
225 	LOG_DBG("*L2CAP CREDITS Peripheral started*");
226 	int err;
227 
228 	/* Prepare tx_data */
229 	for (size_t i = 0; i < sizeof(tx_data); i++) {
230 		tx_data[i] = (uint8_t)i;
231 	}
232 
233 	err = bt_enable(NULL);
234 	if (err) {
235 		FAIL("Can't enable Bluetooth (err %d)", err);
236 		return;
237 	}
238 
239 	LOG_DBG("Peripheral Bluetooth initialized.");
240 	LOG_DBG("Connectable advertising...");
241 	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME_OT, ad, ARRAY_SIZE(ad), NULL, 0);
242 	if (err) {
243 		FAIL("Advertising failed to start (err %d)", err);
244 		return;
245 	}
246 
247 	LOG_DBG("Advertising started.");
248 	LOG_DBG("Peripheral waiting for connection...");
249 	WAIT_FOR_FLAG_SET(is_connected);
250 	LOG_DBG("Peripheral Connected.");
251 
252 	int psm = l2cap_server_register(BT_SECURITY_L1);
253 
254 	LOG_DBG("Registered server PSM %x", psm);
255 
256 	LOG_DBG("Peripheral waiting for transfer completion");
257 	while (rx_cnt < SDU_NUM) {
258 		k_sem_take(&sdu_received, K_FOREVER);
259 
260 		/* Sleep enough so the peer has time to attempt sending another
261 		 * SDU. If it still has credits, it's in its right to do so. If
262 		 * it does so before we release the ref below, then allocation
263 		 * will fail and the channel will be disconnected.
264 		 */
265 		k_sleep(K_SECONDS(5));
266 		LOG_DBG("release SDU ref");
267 		net_buf_unref(test_ctx.rx_sdu);
268 		bt_l2cap_chan_recv_complete(&test_ctx.le_chan.chan, test_ctx.rx_sdu);
269 	}
270 
271 	bt_conn_foreach(BT_CONN_TYPE_LE, disconnect_device, NULL);
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 CREDITS 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 	if (err) {
288 		FAIL("Stop LE scan failed (err %d)", err);
289 		return;
290 	}
291 
292 	char str[BT_ADDR_LE_STR_LEN];
293 
294 	bt_addr_le_to_str(addr, str, sizeof(str));
295 
296 	LOG_DBG("Connecting to %s", str);
297 
298 	param = BT_LE_CONN_PARAM_DEFAULT;
299 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &conn);
300 	if (err) {
301 		FAIL("Create conn failed (err %d)", err);
302 		return;
303 	}
304 }
305 
connect_peripheral(void)306 static void connect_peripheral(void)
307 {
308 	struct bt_le_scan_param scan_param = {
309 		.type = BT_LE_SCAN_TYPE_ACTIVE,
310 		.options = BT_LE_SCAN_OPT_NONE,
311 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
312 		.window = BT_GAP_SCAN_FAST_WINDOW,
313 	};
314 
315 	UNSET_FLAG(is_connected);
316 
317 	int err = bt_le_scan_start(&scan_param, device_found);
318 
319 	ASSERT(!err, "Scanning failed to start (err %d)\n", err);
320 
321 	LOG_DBG("Central initiating connection...");
322 	WAIT_FOR_FLAG_SET(is_connected);
323 }
324 
connect_l2cap_channel(struct bt_conn * conn,void * data)325 static void connect_l2cap_channel(struct bt_conn *conn, void *data)
326 {
327 	int err;
328 	struct bt_l2cap_le_chan *le_chan = &test_ctx.le_chan;
329 
330 	le_chan->chan.ops = &ops;
331 	le_chan->rx.mtu = L2CAP_MTU;
332 
333 	UNSET_FLAG(flag_l2cap_connected);
334 
335 	err = bt_l2cap_chan_connect(conn, &le_chan->chan, 0x0080);
336 	ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
337 
338 	WAIT_FOR_FLAG_SET(flag_l2cap_connected);
339 }
340 
connect_l2cap_ecred_channel(struct bt_conn * conn,void * data)341 static void connect_l2cap_ecred_channel(struct bt_conn *conn, void *data)
342 {
343 	int err;
344 	struct bt_l2cap_le_chan *le_chan = &test_ctx.le_chan;
345 	struct bt_l2cap_chan *chan_list[2] = { &le_chan->chan, 0 };
346 
347 	le_chan->chan.ops = &ops;
348 	le_chan->rx.mtu = L2CAP_MTU;
349 
350 	UNSET_FLAG(flag_l2cap_connected);
351 
352 	err = bt_l2cap_ecred_chan_connect(conn, chan_list, 0x0080);
353 	ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
354 
355 	WAIT_FOR_FLAG_SET(flag_l2cap_connected);
356 }
357 
test_central_main(void)358 static void test_central_main(void)
359 {
360 	LOG_DBG("*L2CAP CREDITS Central started*");
361 	int err;
362 
363 	/* Prepare tx_data */
364 	for (size_t i = 0; i < sizeof(tx_data); i++) {
365 		tx_data[i] = (uint8_t)i;
366 	}
367 
368 	err = bt_enable(NULL);
369 	ASSERT(err == 0, "Can't enable Bluetooth (err %d)\n", err);
370 	LOG_DBG("Central Bluetooth initialized.");
371 
372 	connect_peripheral();
373 
374 	/* Connect L2CAP channels */
375 	LOG_DBG("Connect L2CAP channels");
376 	if (IS_ENABLED(CONFIG_BT_L2CAP_ECRED)) {
377 		bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_ecred_channel, NULL);
378 	} else {
379 		bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_channel, NULL);
380 	}
381 
382 	/* Send SDU_NUM SDUs to each peripheral */
383 	test_ctx.tx_left = SDU_NUM;
384 	l2cap_chan_send(&test_ctx.le_chan.chan, tx_data, sizeof(tx_data));
385 
386 	LOG_DBG("Wait until all transfers are completed.");
387 	while (test_ctx.tx_left) {
388 		k_msleep(100);
389 	}
390 
391 	WAIT_FOR_FLAG_UNSET(is_connected);
392 	LOG_DBG("Peripheral disconnected.");
393 	PASS("L2CAP CREDITS Central passed\n");
394 }
395 
396 static const struct bst_test_instance test_def[] = {
397 	{
398 		.test_id = "peripheral",
399 		.test_descr = "Peripheral L2CAP CREDITS",
400 		.test_post_init_f = test_init,
401 		.test_tick_f = test_tick,
402 		.test_main_f = test_peripheral_main
403 	},
404 	{
405 		.test_id = "central",
406 		.test_descr = "Central L2CAP CREDITS",
407 		.test_post_init_f = test_init,
408 		.test_tick_f = test_tick,
409 		.test_main_f = test_central_main
410 	},
411 	BSTEST_END_MARKER
412 };
413 
test_main_l2cap_credits_install(struct bst_test_list * tests)414 struct bst_test_list *test_main_l2cap_credits_install(struct bst_test_list *tests)
415 {
416 	return bst_add_tests(tests, test_def);
417 }
418 
419 extern struct bst_test_list *test_main_l2cap_credits_install(struct bst_test_list *tests);
420 
421 bst_test_install_t test_installers[] = {
422 	test_main_l2cap_credits_install,
423 	NULL
424 };
425 
main(void)426 int main(void)
427 {
428 	bst_main();
429 
430 	return 0;
431 }
432