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 
test_peripheral_main(void)213 static void test_peripheral_main(void)
214 {
215 	LOG_DBG("*L2CAP CREDITS Peripheral started*");
216 	int err;
217 
218 	/* Prepare tx_data */
219 	for (size_t i = 0; i < sizeof(tx_data); i++) {
220 		tx_data[i] = (uint8_t)i;
221 	}
222 
223 	err = bt_enable(NULL);
224 	if (err) {
225 		FAIL("Can't enable Bluetooth (err %d)", err);
226 		return;
227 	}
228 
229 	LOG_DBG("Peripheral Bluetooth initialized.");
230 	LOG_DBG("Connectable advertising...");
231 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, NULL, 0, NULL, 0);
232 	if (err) {
233 		FAIL("Advertising failed to start (err %d)", err);
234 		return;
235 	}
236 
237 	LOG_DBG("Advertising started.");
238 	LOG_DBG("Peripheral waiting for connection...");
239 	WAIT_FOR_FLAG_SET(is_connected);
240 	LOG_DBG("Peripheral Connected.");
241 
242 	int psm = l2cap_server_register(BT_SECURITY_L1);
243 
244 	LOG_DBG("Registered server PSM %x", psm);
245 
246 	LOG_DBG("Peripheral waiting for transfer completion");
247 	while (rx_cnt < SDU_NUM) {
248 		k_sem_take(&sdu_received, K_FOREVER);
249 
250 		/* Sleep enough so the peer has time to attempt sending another
251 		 * SDU. If it still has credits, it's in its right to do so. If
252 		 * it does so before we release the ref below, then allocation
253 		 * will fail and the channel will be disconnected.
254 		 */
255 		k_sleep(K_SECONDS(5));
256 		LOG_DBG("release SDU ref");
257 		net_buf_unref(test_ctx.rx_sdu);
258 		bt_l2cap_chan_recv_complete(&test_ctx.le_chan.chan, test_ctx.rx_sdu);
259 	}
260 
261 	bt_conn_foreach(BT_CONN_TYPE_LE, disconnect_device, NULL);
262 	LOG_INF("Total received: %d", rx_cnt);
263 
264 	ASSERT(rx_cnt == SDU_NUM, "Did not receive expected no of SDUs\n");
265 
266 	PASS("L2CAP CREDITS Peripheral passed\n");
267 }
268 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)269 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
270 			 struct net_buf_simple *ad)
271 {
272 	struct bt_le_conn_param *param;
273 	struct bt_conn *conn;
274 	int err;
275 
276 	err = bt_le_scan_stop();
277 	if (err) {
278 		FAIL("Stop LE scan failed (err %d)", err);
279 		return;
280 	}
281 
282 	char str[BT_ADDR_LE_STR_LEN];
283 
284 	bt_addr_le_to_str(addr, str, sizeof(str));
285 
286 	LOG_DBG("Connecting to %s", str);
287 
288 	param = BT_LE_CONN_PARAM_DEFAULT;
289 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &conn);
290 	if (err) {
291 		FAIL("Create conn failed (err %d)", err);
292 		return;
293 	}
294 }
295 
connect_peripheral(void)296 static void connect_peripheral(void)
297 {
298 	struct bt_le_scan_param scan_param = {
299 		.type = BT_LE_SCAN_TYPE_ACTIVE,
300 		.options = BT_LE_SCAN_OPT_NONE,
301 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
302 		.window = BT_GAP_SCAN_FAST_WINDOW,
303 	};
304 
305 	UNSET_FLAG(is_connected);
306 
307 	int err = bt_le_scan_start(&scan_param, device_found);
308 
309 	ASSERT(!err, "Scanning failed to start (err %d)\n", err);
310 
311 	LOG_DBG("Central initiating connection...");
312 	WAIT_FOR_FLAG_SET(is_connected);
313 }
314 
connect_l2cap_channel(struct bt_conn * conn,void * data)315 static void connect_l2cap_channel(struct bt_conn *conn, void *data)
316 {
317 	int err;
318 	struct bt_l2cap_le_chan *le_chan = &test_ctx.le_chan;
319 
320 	le_chan->chan.ops = &ops;
321 	le_chan->rx.mtu = L2CAP_MTU;
322 
323 	UNSET_FLAG(flag_l2cap_connected);
324 
325 	err = bt_l2cap_chan_connect(conn, &le_chan->chan, 0x0080);
326 	ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
327 
328 	WAIT_FOR_FLAG_SET(flag_l2cap_connected);
329 }
330 
connect_l2cap_ecred_channel(struct bt_conn * conn,void * data)331 static void connect_l2cap_ecred_channel(struct bt_conn *conn, void *data)
332 {
333 	int err;
334 	struct bt_l2cap_le_chan *le_chan = &test_ctx.le_chan;
335 	struct bt_l2cap_chan *chan_list[2] = { &le_chan->chan, 0 };
336 
337 	le_chan->chan.ops = &ops;
338 	le_chan->rx.mtu = L2CAP_MTU;
339 
340 	UNSET_FLAG(flag_l2cap_connected);
341 
342 	err = bt_l2cap_ecred_chan_connect(conn, chan_list, 0x0080);
343 	ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
344 
345 	WAIT_FOR_FLAG_SET(flag_l2cap_connected);
346 }
347 
test_central_main(void)348 static void test_central_main(void)
349 {
350 	LOG_DBG("*L2CAP CREDITS Central started*");
351 	int err;
352 
353 	/* Prepare tx_data */
354 	for (size_t i = 0; i < sizeof(tx_data); i++) {
355 		tx_data[i] = (uint8_t)i;
356 	}
357 
358 	err = bt_enable(NULL);
359 	ASSERT(err == 0, "Can't enable Bluetooth (err %d)\n", err);
360 	LOG_DBG("Central Bluetooth initialized.");
361 
362 	connect_peripheral();
363 
364 	/* Connect L2CAP channels */
365 	LOG_DBG("Connect L2CAP channels");
366 	if (IS_ENABLED(CONFIG_BT_L2CAP_ECRED)) {
367 		bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_ecred_channel, NULL);
368 	} else {
369 		bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_channel, NULL);
370 	}
371 
372 	/* Send SDU_NUM SDUs to each peripheral */
373 	test_ctx.tx_left = SDU_NUM;
374 	l2cap_chan_send(&test_ctx.le_chan.chan, tx_data, sizeof(tx_data));
375 
376 	LOG_DBG("Wait until all transfers are completed.");
377 	while (test_ctx.tx_left) {
378 		k_msleep(100);
379 	}
380 
381 	WAIT_FOR_FLAG_UNSET(is_connected);
382 	LOG_DBG("Peripheral disconnected.");
383 	PASS("L2CAP CREDITS Central passed\n");
384 }
385 
386 static const struct bst_test_instance test_def[] = {
387 	{
388 		.test_id = "peripheral",
389 		.test_descr = "Peripheral L2CAP CREDITS",
390 		.test_pre_init_f = test_init,
391 		.test_tick_f = test_tick,
392 		.test_main_f = test_peripheral_main
393 	},
394 	{
395 		.test_id = "central",
396 		.test_descr = "Central L2CAP CREDITS",
397 		.test_pre_init_f = test_init,
398 		.test_tick_f = test_tick,
399 		.test_main_f = test_central_main
400 	},
401 	BSTEST_END_MARKER
402 };
403 
test_main_l2cap_credits_install(struct bst_test_list * tests)404 struct bst_test_list *test_main_l2cap_credits_install(struct bst_test_list *tests)
405 {
406 	return bst_add_tests(tests, test_def);
407 }
408 
409 extern struct bst_test_list *test_main_l2cap_credits_install(struct bst_test_list *tests);
410 
411 bst_test_install_t test_installers[] = {
412 	test_main_l2cap_credits_install,
413 	NULL
414 };
415 
main(void)416 int main(void)
417 {
418 	bst_main();
419 
420 	return 0;
421 }
422