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