1 /*
2  * Copyright (c) 2024 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "bstests.h"
8 #include "babblekit/testcase.h"
9 #include <zephyr/bluetooth/conn.h>
10 #include <zephyr/bluetooth/hci.h>
11 
12 /* Include hci_common_internal for the purpose of checking HCI Command counts. */
13 #include "common/hci_common_internal.h"
14 
15 /* Include conn_internal for the purpose of checking reference counts. */
16 #include "host/conn_internal.h"
17 
18 struct bst_test_list *test_peripheral_install(struct bst_test_list *tests);
19 
20 static K_SEM_DEFINE(sem_failed_to_connect, 0, 1);
21 static K_SEM_DEFINE(sem_connected, 0, 1);
22 
connected_cb_expect_fail(struct bt_conn * conn,uint8_t err)23 static void connected_cb_expect_fail(struct bt_conn *conn, uint8_t err)
24 {
25 	TEST_ASSERT(conn);
26 	TEST_ASSERT(err == BT_HCI_ERR_UNKNOWN_CONN_ID, "Expected connection timeout");
27 
28 	k_sem_give(&sem_failed_to_connect);
29 	bt_conn_unref(conn);
30 }
31 
connected_cb(struct bt_conn * conn,uint8_t err)32 static void connected_cb(struct bt_conn *conn, uint8_t err)
33 {
34 	TEST_ASSERT(conn);
35 	TEST_ASSERT(err == BT_HCI_ERR_SUCCESS, "Expected connection establishment");
36 
37 	k_sem_give(&sem_connected);
38 	bt_conn_unref(conn);
39 }
40 
41 static struct bt_conn_cb conn_cb_expect_fail = {
42 	.connected = connected_cb_expect_fail,
43 };
44 
45 static struct bt_conn_cb conn_cb = {
46 	.connected = connected_cb,
47 };
48 
test_central_connect_timeout_with_timeout(uint32_t timeout_ms,bool stack_load)49 static void test_central_connect_timeout_with_timeout(uint32_t timeout_ms, bool stack_load)
50 {
51 	int err;
52 	struct bt_conn *conn;
53 
54 	/* A zero value for `bt_conn_le_create_param.timeout` shall be
55 	 * interpreted as `CONFIG_BT_CREATE_CONN_TIMEOUT`.
56 	 */
57 	uint32_t expected_conn_timeout_ms =
58 		timeout_ms ? timeout_ms : CONFIG_BT_CREATE_CONN_TIMEOUT * MSEC_PER_SEC;
59 
60 	bt_addr_le_t peer = {.a.val = {0x01}};
61 	const struct bt_conn_le_create_param create_param = {
62 		.options = BT_CONN_LE_OPT_NONE,
63 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
64 		.window = BT_GAP_SCAN_FAST_WINDOW,
65 		.interval_coded = 0,
66 		.window_coded = 0,
67 		.timeout = timeout_ms / 10,
68 	};
69 	struct net_buf *bufs[BT_BUF_CMD_TX_COUNT];
70 
71 	k_sem_reset(&sem_failed_to_connect);
72 
73 	const uint64_t conn_create_start = k_uptime_get();
74 
75 	err = bt_conn_le_create(&peer, &create_param, BT_LE_CONN_PARAM_DEFAULT, &conn);
76 	TEST_ASSERT(err == 0, "Failed starting initiator (err %d)", err);
77 
78 	if (stack_load) {
79 		/* Claim all the buffers so that the stack cannot handle the timeout */
80 		for (int i = 0; i < BT_BUF_CMD_TX_COUNT; i++) {
81 			bufs[i] = bt_hci_cmd_alloc(K_FOREVER);
82 			TEST_ASSERT(bufs[i] != NULL, "Failed to claim all command buffers");
83 		}
84 		/* Hold all the buffers until after we expect the connection to timeout */
85 		err = k_sem_take(&sem_failed_to_connect, K_MSEC(expected_conn_timeout_ms + 50));
86 		TEST_ASSERT(err == -EAGAIN, "Callback ran with no buffers available", err);
87 		/* Release all the buffers back to the stack */
88 		for (int i = 0; i < BT_BUF_CMD_TX_COUNT; i++) {
89 			net_buf_unref(bufs[i]);
90 		}
91 	}
92 
93 	err = k_sem_take(&sem_failed_to_connect, K_MSEC(2 * expected_conn_timeout_ms));
94 	TEST_ASSERT(err == 0, "Failed getting connected timeout within %d s (err %d)",
95 		    2 * expected_conn_timeout_ms, err);
96 
97 	const uint64_t conn_create_end = k_uptime_get();
98 
99 	const int64_t time_diff_ms = conn_create_end - conn_create_start;
100 	const int64_t diff_to_expected_ms = abs(time_diff_ms - expected_conn_timeout_ms);
101 
102 	TEST_PRINT("Connection timeout after %d ms", time_diff_ms);
103 	TEST_ASSERT(diff_to_expected_ms < 0.1 * expected_conn_timeout_ms,
104 		    "Connection timeout not within 10%% of expected timeout. "
105 		    "Actual timeout: %d",
106 		    time_diff_ms);
107 }
108 
test_central_connect_timeout(void)109 static void test_central_connect_timeout(void)
110 {
111 	int err;
112 
113 	bt_conn_cb_register(&conn_cb_expect_fail);
114 
115 	/* Initialize Bluetooth */
116 	err = bt_enable(NULL);
117 	TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
118 
119 	test_central_connect_timeout_with_timeout(0, false);
120 	test_central_connect_timeout_with_timeout(1000, false);
121 	test_central_connect_timeout_with_timeout(2000, true);
122 
123 	TEST_PASS("Correct timeout");
124 }
125 
test_central_connect_when_connecting(void)126 static void test_central_connect_when_connecting(void)
127 {
128 	int err;
129 
130 	bt_conn_cb_register(&conn_cb_expect_fail);
131 
132 	/* Initialize Bluetooth */
133 	err = bt_enable(NULL);
134 	TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
135 
136 	struct bt_conn *conn;
137 
138 	bt_addr_le_t peer = {.a.val = {0x01}};
139 
140 	const struct bt_conn_le_create_param create_param = {
141 		.options = BT_CONN_LE_OPT_NONE,
142 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
143 		.window = BT_GAP_SCAN_FAST_WINDOW,
144 	};
145 
146 	k_sem_reset(&sem_failed_to_connect);
147 
148 	err = bt_conn_le_create(&peer, &create_param, BT_LE_CONN_PARAM_DEFAULT, &conn);
149 	TEST_ASSERT(err == 0, "Failed starting initiator (err %d)", err);
150 
151 	/* Now we have a valid connection reference */
152 	atomic_val_t initial_refs = atomic_get(&conn->ref);
153 
154 	TEST_ASSERT(initial_refs >= 1, "Expect to have at least once reference");
155 
156 	err = bt_conn_le_create(&peer, &create_param, BT_LE_CONN_PARAM_DEFAULT, &conn);
157 	TEST_ASSERT(err == -EALREADY, "Expected to fail to create connection (err %d)", err);
158 
159 	/* Expect the number of refs to be unchanged. */
160 	TEST_ASSERT(atomic_get(&conn->ref) == initial_refs,
161 		    "Expect number of references to be unchanged");
162 
163 	err = k_sem_take(&sem_failed_to_connect, K_FOREVER);
164 	TEST_ASSERT(err == 0, "Failed getting connected timeout", err);
165 
166 	TEST_ASSERT(atomic_get(&conn->ref) == 0, "Expect no more references");
167 
168 	TEST_PASS("Passed");
169 }
170 
test_central_connect_to_existing(void)171 static void test_central_connect_to_existing(void)
172 {
173 	int err;
174 
175 	bt_conn_cb_register(&conn_cb);
176 
177 	/* Initialize Bluetooth */
178 	err = bt_enable(NULL);
179 	TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
180 
181 	struct bt_conn *conn;
182 
183 	bt_addr_le_t peer = {.type = BT_ADDR_LE_RANDOM,
184 			     .a.val = {0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}};
185 
186 	const struct bt_conn_le_create_param create_param = {
187 		.options = BT_CONN_LE_OPT_NONE,
188 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
189 		.window = BT_GAP_SCAN_FAST_WINDOW,
190 	};
191 
192 	k_sem_reset(&sem_connected);
193 
194 	err = bt_conn_le_create(&peer, &create_param, BT_LE_CONN_PARAM_DEFAULT, &conn);
195 	TEST_ASSERT(err == 0, "Failed starting initiator (err %d)", err);
196 
197 	err = k_sem_take(&sem_connected, K_FOREVER);
198 	TEST_ASSERT(err == 0, "Failed establishing connection", err);
199 
200 	/* Now we have a valid connection reference */
201 	atomic_val_t initial_refs = atomic_get(&conn->ref);
202 
203 	TEST_ASSERT(initial_refs >= 1, "Expect to have at least once reference");
204 
205 	err = bt_conn_le_create(&peer, &create_param, BT_LE_CONN_PARAM_DEFAULT, &conn);
206 	TEST_ASSERT(err == -EINVAL, "Expected to fail to create a connection (err %d)", err);
207 
208 	/* Expect the number of refs to be unchanged. */
209 	TEST_ASSERT(atomic_get(&conn->ref) == initial_refs,
210 		    "Expect number of references to be unchanged");
211 
212 	TEST_PASS("Passed");
213 }
214 
215 static const struct bst_test_instance test_def[] = {
216 	{
217 		.test_id = "central_connect_timeout",
218 		.test_descr = "Verifies that the default connection timeout is used correctly",
219 		.test_main_f = test_central_connect_timeout,
220 	},
221 	{
222 		.test_id = "central_connect_when_connecting",
223 		.test_descr = "Verifies that the stack returns an error code when trying to connect"
224 			      " while already connecting",
225 		.test_main_f = test_central_connect_when_connecting,
226 	},
227 	{
228 		.test_id = "central_connect_to_existing",
229 		.test_descr =
230 			"Verifies that the stack returns an error code when trying to connect"
231 			" to an existing device and does not unref the existing connection object.",
232 		.test_main_f = test_central_connect_to_existing,
233 	},
234 	BSTEST_END_MARKER,
235 };
236 
test_central_install(struct bst_test_list * tests)237 static struct bst_test_list *test_central_install(struct bst_test_list *tests)
238 {
239 	return bst_add_tests(tests, test_def);
240 }
241 
242 bst_test_install_t test_installers[] = {test_central_install, test_peripheral_install, NULL};
243 
main(void)244 int main(void)
245 {
246 	bst_main();
247 	return 0;
248 }
249