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