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 "testlib/conn.h"
14 #include "testlib/scan.h"
15 
16 #include "babblekit/flags.h"
17 #include "babblekit/testcase.h"
18 
19 /* local includes */
20 #include "data.h"
21 
22 LOG_MODULE_REGISTER(dut, CONFIG_APP_LOG_LEVEL);
23 
24 #define NUM_TESTERS CONFIG_BT_MAX_CONN
25 
26 /* Build with the minimum possible amount of RX buffers */
27 BUILD_ASSERT(BT_BUF_ACL_RX_COUNT >= (CONFIG_BT_MAX_CONN + 1));
28 
29 struct tester {
30 	size_t sdu_count;
31 	struct bt_conn *conn;
32 	struct bt_l2cap_le_chan le_chan;
33 };
34 
35 static struct tester testers[NUM_TESTERS];
36 
get_tester(struct bt_conn * conn)37 static struct tester *get_tester(struct bt_conn *conn)
38 {
39 	for (size_t i = 0; i < ARRAY_SIZE(testers); i++) {
40 		if (testers[i].conn == conn) {
41 			return &testers[i];
42 		}
43 	}
44 
45 	return NULL;
46 }
47 
sent_cb(struct bt_l2cap_chan * chan)48 static void sent_cb(struct bt_l2cap_chan *chan)
49 {
50 	TEST_FAIL("Tester should not send data");
51 }
52 
recv_cb(struct bt_l2cap_chan * chan,struct net_buf * buf)53 static int recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
54 {
55 	char addr[BT_ADDR_LE_STR_LEN];
56 	struct tester *tester = get_tester(chan->conn);
57 
58 	tester->sdu_count += 1;
59 
60 	bt_addr_le_to_str(bt_conn_get_dst(chan->conn), addr, sizeof(addr));
61 
62 	LOG_INF("Received SDU %d / %d from (%s)", tester->sdu_count, SDU_NUM, addr);
63 
64 	return 0;
65 }
66 
l2cap_chan_connected_cb(struct bt_l2cap_chan * chan)67 static void l2cap_chan_connected_cb(struct bt_l2cap_chan *chan)
68 {
69 	LOG_DBG("%p", chan);
70 }
71 
l2cap_chan_disconnected_cb(struct bt_l2cap_chan * chan)72 static void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
73 {
74 	LOG_DBG("%p", chan);
75 }
76 
server_accept_cb(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)77 static int server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
78 			    struct bt_l2cap_chan **chan)
79 {
80 	static struct bt_l2cap_chan_ops ops = {
81 		.connected = l2cap_chan_connected_cb,
82 		.disconnected = l2cap_chan_disconnected_cb,
83 		.recv = recv_cb,
84 		.sent = sent_cb,
85 	};
86 
87 	struct tester *tester = get_tester(conn);
88 	struct bt_l2cap_le_chan *le_chan = &tester->le_chan;
89 
90 	memset(le_chan, 0, sizeof(*le_chan));
91 	le_chan->chan.ops = &ops;
92 	*chan = &le_chan->chan;
93 
94 	return 0;
95 }
96 
l2cap_server_register(bt_security_t sec_level)97 static int l2cap_server_register(bt_security_t sec_level)
98 {
99 	static struct bt_l2cap_server test_l2cap_server = {.accept = server_accept_cb};
100 
101 	test_l2cap_server.psm = L2CAP_TEST_PSM;
102 	test_l2cap_server.sec_level = sec_level;
103 
104 	int err = bt_l2cap_server_register(&test_l2cap_server);
105 
106 	TEST_ASSERT(err == 0, "Failed to register l2cap server (err %d)", err);
107 
108 	return test_l2cap_server.psm;
109 }
110 
connect_tester(void)111 static struct bt_conn *connect_tester(void)
112 {
113 	int err;
114 	bt_addr_le_t tester = {};
115 	struct bt_conn *conn = NULL;
116 	char addr[BT_ADDR_LE_STR_LEN];
117 
118 	/* The device address will not change. Scan only once in order to reduce
119 	 * test time.
120 	 */
121 	err = bt_testlib_scan_find_name(&tester, TESTER_NAME);
122 	TEST_ASSERT(!err, "Failed to start scan (err %d)", err);
123 
124 	/* Create a connection using that address */
125 	err = bt_testlib_connect(&tester, &conn);
126 	TEST_ASSERT(!err, "Failed to initiate connection (err %d)", err);
127 
128 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
129 	LOG_DBG("Connected to %s", addr);
130 
131 	return conn;
132 }
133 
all_data_transferred(void)134 static bool all_data_transferred(void)
135 {
136 	size_t total_sdu_count = 0;
137 
138 	for (size_t i = 0; i < ARRAY_SIZE(testers); i++) {
139 		total_sdu_count += testers[i].sdu_count;
140 	}
141 
142 	TEST_ASSERT(total_sdu_count <= (SDU_NUM * NUM_TESTERS), "Received more SDUs than expected");
143 
144 	return total_sdu_count == (SDU_NUM * NUM_TESTERS);
145 }
146 
entrypoint_dut(void)147 void entrypoint_dut(void)
148 {
149 	/* Multilink Host Flow Control (HFC) test
150 	 *
151 	 * Test purpose:
152 	 *
153 	 * Verifies that we are able to do L2CAP recombination on multiple links
154 	 * when we have the smallest possible amount of ACL buffers.
155 	 *
156 	 * Devices:
157 	 * - `dut`: receives L2CAP PDUs from testers
158 	 * - `tester`: send ACL packets (parts of large L2CAP PDU) very slowly
159 	 *
160 	 * Procedure:
161 	 *
162 	 * DUT:
163 	 * - establish connection to tester
164 	 * - [acl connected]
165 	 * - establish L2CAP channel
166 	 * - [l2 connected]
167 	 * - receive L2CAP PDUs until SDU_NUM is reached
168 	 * - mark test as passed and terminate simulation
169 	 *
170 	 * tester 0/1/2:
171 	 * - scan & connect ACL
172 	 * - [acl connected]
173 	 * - [l2cap dynamic channel connected]
174 	 * (and then in a loop)
175 	 * - send part of L2CAP PDU
176 	 * - wait a set amount of time
177 	 * - exit loop when SDU_NUM sent
178 	 *
179 	 * [verdict]
180 	 * - dut application is able to receive all expected L2CAP packets from
181 	 *   the testers
182 	 */
183 	int err;
184 
185 	/* Mark test as in progress. */
186 	TEST_START("dut");
187 
188 	/* Initialize Bluetooth */
189 	err = bt_enable(NULL);
190 	TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
191 
192 	LOG_DBG("Bluetooth initialized");
193 
194 	int psm = l2cap_server_register(BT_SECURITY_L1);
195 
196 	LOG_DBG("Registered server PSM %x", psm);
197 
198 	for (size_t i = 0; i < ARRAY_SIZE(testers); i++) {
199 		LOG_DBG("Connecting tester %d", i);
200 		testers[i].sdu_count = 0;
201 		testers[i].conn = connect_tester();
202 	}
203 
204 	LOG_DBG("Connected all testers");
205 
206 	while (!all_data_transferred()) {
207 		/* Wait until we have received all expected data. */
208 		k_sleep(K_MSEC(100));
209 	}
210 
211 	TEST_PASS_AND_EXIT("dut");
212 }
213