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