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