1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/bluetooth/bluetooth.h>
9 #include <zephyr/bluetooth/conn.h>
10 #include <zephyr/bluetooth/l2cap.h>
11 #include <zephyr/logging/log.h>
12
13 #include "babblekit/flags.h"
14 #include "babblekit/testcase.h"
15
16 /* local includes */
17 #include "data.h"
18
19 LOG_MODULE_REGISTER(dut, CONFIG_APP_LOG_LEVEL);
20
21 DEFINE_FLAG_STATIC(ADVERTISING);
22
sdu_destroy(struct net_buf * buf)23 static void sdu_destroy(struct net_buf *buf)
24 {
25 LOG_DBG("%p", buf);
26
27 net_buf_destroy(buf);
28 }
29
30 /* Only one SDU per link will be transmitted at a time */
31 NET_BUF_POOL_DEFINE(sdu_tx_pool, CONFIG_BT_MAX_CONN, BT_L2CAP_SDU_BUF_SIZE(SDU_LEN),
32 CONFIG_BT_CONN_TX_USER_DATA_SIZE, sdu_destroy);
33
34 static uint8_t tx_data[SDU_LEN];
35
36 struct test_ctx {
37 bt_addr_le_t peer;
38 struct bt_l2cap_le_chan le_chan;
39 size_t sdu_count; /* the number of SDUs that have been transferred until now */
40 };
41
42 static struct test_ctx contexts[CONFIG_BT_MAX_CONN];
43
send_data_over_l2cap(struct bt_l2cap_chan * chan,uint8_t * data,size_t len)44 static int send_data_over_l2cap(struct bt_l2cap_chan *chan, uint8_t *data, size_t len)
45 {
46 char addr[BT_ADDR_LE_STR_LEN];
47
48 bt_addr_le_to_str(bt_conn_get_dst(chan->conn), addr, sizeof(addr));
49
50 LOG_DBG("[%s] chan %p data %p len %d", addr, chan, data, len);
51
52 struct net_buf *buf = net_buf_alloc(&sdu_tx_pool, K_NO_WAIT);
53
54 if (buf == NULL) {
55 TEST_FAIL("No more memory");
56 return -ENOMEM;
57 }
58
59 net_buf_reserve(buf, BT_L2CAP_SDU_CHAN_SEND_RESERVE);
60 net_buf_add_mem(buf, data, len);
61
62 int ret = bt_l2cap_chan_send(chan, buf);
63
64 TEST_ASSERT(ret == 0, "Failed sending: err %d", ret);
65 LOG_DBG("queued SDU", len);
66
67 return ret;
68 }
69
resume_sending_until_done(struct test_ctx * ctx)70 static void resume_sending_until_done(struct test_ctx *ctx)
71 {
72 struct bt_l2cap_chan *chan = &ctx->le_chan.chan;
73
74 TEST_ASSERT(ctx->le_chan.state == BT_L2CAP_CONNECTED,
75 "attempting to send on disconnected channel (%p)", chan);
76
77 LOG_DBG("%p, transmitted %d SDUs", chan, ctx->sdu_count);
78
79 if (ctx->sdu_count < SDU_NUM) {
80 send_data_over_l2cap(chan, tx_data, sizeof(tx_data));
81 } else {
82 char addr[BT_ADDR_LE_STR_LEN];
83
84 bt_addr_le_to_str(bt_conn_get_dst(chan->conn), addr, sizeof(addr));
85
86 LOG_DBG("[%s] Done sending", addr);
87 }
88 }
89
get_ctx_from_chan(struct bt_l2cap_chan * chan)90 static struct test_ctx *get_ctx_from_chan(struct bt_l2cap_chan *chan)
91 {
92 struct bt_l2cap_le_chan *le_chan = CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan);
93 struct test_ctx *ctx = CONTAINER_OF(le_chan, struct test_ctx, le_chan);
94
95 TEST_ASSERT(PART_OF_ARRAY(contexts, ctx), "memory corruption");
96
97 return ctx;
98 }
99
sent_cb(struct bt_l2cap_chan * chan)100 static void sent_cb(struct bt_l2cap_chan *chan)
101 {
102 struct test_ctx *ctx = get_ctx_from_chan(chan);
103
104 LOG_DBG("%p", chan);
105
106 ctx->sdu_count++;
107
108 resume_sending_until_done(ctx);
109 }
110
l2cap_chan_connected_cb(struct bt_l2cap_chan * chan)111 static void l2cap_chan_connected_cb(struct bt_l2cap_chan *chan)
112 {
113 struct bt_l2cap_le_chan *le_chan = CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan);
114
115 LOG_DBG("%p (tx mtu %d mps %d) (tx mtu %d mps %d)", chan, le_chan->tx.mtu, le_chan->tx.mps,
116 le_chan->rx.mtu, le_chan->rx.mps);
117
118 LOG_DBG("initiating SDU transfer");
119 resume_sending_until_done(get_ctx_from_chan(chan));
120 }
121
l2cap_chan_disconnected_cb(struct bt_l2cap_chan * chan)122 static void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
123 {
124 LOG_DBG("%p", chan);
125 }
126
recv_cb(struct bt_l2cap_chan * chan,struct net_buf * buf)127 static int recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
128 {
129 TEST_FAIL("DUT should not receive data");
130
131 return 0;
132 }
133
connect_l2cap_channel(struct bt_conn * conn,struct bt_l2cap_le_chan * le_chan)134 static int connect_l2cap_channel(struct bt_conn *conn, struct bt_l2cap_le_chan *le_chan)
135 {
136 static struct bt_l2cap_chan_ops ops = {
137 .connected = l2cap_chan_connected_cb,
138 .disconnected = l2cap_chan_disconnected_cb,
139 .recv = recv_cb,
140 .sent = sent_cb,
141 };
142
143 memset(le_chan, 0, sizeof(*le_chan));
144 le_chan->chan.ops = &ops;
145
146 return bt_l2cap_chan_connect(conn, &le_chan->chan, 0x0080);
147 }
148
addr_in_use(bt_addr_le_t * address)149 static bool addr_in_use(bt_addr_le_t *address)
150 {
151 return !bt_addr_le_eq(address, BT_ADDR_LE_ANY);
152 }
153
alloc_ctx(void)154 static struct test_ctx *alloc_ctx(void)
155 {
156 for (size_t i = 0; i < ARRAY_SIZE(contexts); i++) {
157 struct test_ctx *context = &contexts[i];
158 struct bt_l2cap_le_chan *le_chan = &context->le_chan;
159
160 if (le_chan->state != BT_L2CAP_DISCONNECTED) {
161 continue;
162 }
163
164 if (addr_in_use(&context->peer)) {
165 continue;
166 }
167
168 memset(context, 0, sizeof(struct test_ctx));
169
170 return context;
171 }
172
173 return NULL;
174 }
175
get_ctx_from_address(const bt_addr_le_t * address)176 static struct test_ctx *get_ctx_from_address(const bt_addr_le_t *address)
177 {
178 for (size_t i = 0; i < ARRAY_SIZE(contexts); i++) {
179 struct test_ctx *context = &contexts[i];
180
181 if (bt_addr_le_eq(address, &context->peer)) {
182 return context;
183 }
184 }
185
186 return NULL;
187 }
188
acl_connected(struct bt_conn * conn,uint8_t err)189 static void acl_connected(struct bt_conn *conn, uint8_t err)
190 {
191 const bt_addr_le_t *central = bt_conn_get_dst(conn);
192 char addr[BT_ADDR_LE_STR_LEN];
193 struct test_ctx *ctx;
194 int ret;
195
196 bt_addr_le_to_str(central, addr, sizeof(addr));
197
198 TEST_ASSERT(err == 0, "Failed to connect to %s (0x%02x)", addr, err);
199
200 UNSET_FLAG(ADVERTISING);
201
202 LOG_DBG("[%s] Connected (conn %p)", addr, conn);
203
204 ctx = get_ctx_from_address(central);
205 if (ctx == NULL) {
206 LOG_DBG("no initialized context for %s, allocating..", addr);
207 ctx = alloc_ctx();
208 TEST_ASSERT(ctx, "Couldn't allocate ctx for conn %p", conn);
209
210 LOG_DBG("allocated context %p for %s", ctx, central);
211 bt_addr_le_copy(&ctx->peer, central);
212 }
213
214 ret = connect_l2cap_channel(conn, &ctx->le_chan);
215 TEST_ASSERT(!ret, "Error connecting l2cap channel (err %d)", ret);
216 }
217
acl_disconnected(struct bt_conn * conn,uint8_t reason)218 static void acl_disconnected(struct bt_conn *conn, uint8_t reason)
219 {
220 char addr[BT_ADDR_LE_STR_LEN];
221
222 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
223
224 LOG_DBG("Disconnected from %s (reason 0x%02x)", addr, reason);
225 }
226
increment(struct bt_conn * conn,void * user_data)227 static void increment(struct bt_conn *conn, void *user_data)
228 {
229 size_t *conn_count = user_data;
230
231 (*conn_count)++;
232 }
233
have_free_conn(void)234 static bool have_free_conn(void)
235 {
236 size_t conn_count = 0;
237
238 bt_conn_foreach(BT_CONN_TYPE_LE, increment, &conn_count);
239
240 return conn_count < CONFIG_BT_MAX_CONN;
241 }
242
243 static const struct bt_data ad[] = {
244 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
245 BT_DATA(BT_DATA_NAME_COMPLETE, DUT_NAME, sizeof(DUT_NAME) - 1),
246 };
247
start_advertising(void)248 static void start_advertising(void)
249 {
250 int err;
251
252 LOG_DBG("starting advertiser");
253
254 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
255 TEST_ASSERT(!err, "Advertising failed to start (err %d)", err);
256 }
257
all_data_transferred(void)258 static bool all_data_transferred(void)
259 {
260 size_t total_sdu_count = 0;
261
262 for (size_t i = 0; i < ARRAY_SIZE(contexts); i++) {
263 total_sdu_count += contexts[i].sdu_count;
264 }
265
266 TEST_ASSERT(total_sdu_count <= (SDU_NUM * CONFIG_BT_MAX_CONN),
267 "Received more SDUs than expected");
268
269 return total_sdu_count == (SDU_NUM * CONFIG_BT_MAX_CONN);
270 }
271
entrypoint_dut(void)272 void entrypoint_dut(void)
273 {
274 /* Test purpose:
275 *
276 * For a peripheral device (DUT) that has multiple ACL connections to
277 * central devices: Verify that the data streams on one connection are
278 * not affected by one of the centrals going out of range or not
279 * responding.
280 *
281 * Three devices:
282 * - `dut`: sends L2CAP packets to p0 and p1
283 *
284 * DUT (in a loop):
285 * - advertise as connectable
286 * - [acl connected]
287 * - establish L2CAP channel
288 * - [l2 connected]
289 * - send L2CAP data until ACL disconnected or SDU_NUM SDUs reached
290 *
291 * p0/1/2 (in a loop):
292 * - scan & connect ACL
293 * - [acl connected]
294 * - [l2cap dynamic channel connected]
295 * - receive data from DUT
296 * - disconnect
297 *
298 * Verdict:
299 * - DUT is able to transfer SDU_NUM SDUs to all peers. Data can be
300 * dropped but resources should not leak, and the transfer should not
301 * stall.
302 */
303 int err;
304 static struct bt_conn_cb peripheral_cb = {
305 .connected = acl_connected,
306 .disconnected = acl_disconnected,
307 };
308
309 /* Mark test as in progress. */
310 TEST_START("dut");
311
312 /* Initialize Bluetooth */
313 err = bt_conn_cb_register(&peripheral_cb);
314 TEST_ASSERT(err == 0, "Can't register callbacks (err %d)", err);
315
316 err = bt_enable(NULL);
317 TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
318
319 LOG_DBG("Bluetooth initialized");
320
321 while (!all_data_transferred()) {
322 if (!have_free_conn() || IS_FLAG_SET(ADVERTISING)) {
323 /* Sleep to not hammer the CPU checking the `if` */
324 k_sleep(K_MSEC(10));
325 continue;
326 }
327
328 start_advertising();
329 SET_FLAG(ADVERTISING);
330
331 /* L2 channel is opened from conn->connected() */
332 /* L2 data transfer is initiated from l2->connected() */
333 /* L2 data transfer is initiated for next SDU from l2->sent() */
334
335 }
336
337 TEST_PASS_AND_EXIT("dut");
338 }
339