1 /* Copyright (c) 2024 Nordic Semiconductor ASA
2  * SPDX-License-Identifier: Apache-2.0
3  */
4 
5 #include <argparse.h>
6 #include <zephyr/bluetooth/gatt.h>
7 #include <zephyr/logging/log.h>
8 #include <zephyr/sys/__assert.h>
9 #include <zephyr/settings/settings.h>
10 #include <zephyr/sys/byteorder.h>
11 #include <zephyr/bluetooth/bluetooth.h>
12 
13 #include "host/att_internal.h"
14 
15 #include "testlib/adv.h"
16 #include "testlib/att_read.h"
17 #include "testlib/att_write.h"
18 #include "bs_macro.h"
19 #include "bs_sync.h"
20 #include <testlib/conn.h>
21 #include "testlib/log_utils.h"
22 #include "testlib/scan.h"
23 #include "testlib/security.h"
24 
25 /* This test uses system asserts to fail tests. */
26 BUILD_ASSERT(__ASSERT_ON);
27 
28 LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
29 
30 #define CENTRAL_DEVICE_NBR    0
31 #define PERIPHERAL_DEVICE_NBR 1
32 
33 #define UUID_1                                                                                     \
34 	BT_UUID_DECLARE_128(0xdb, 0x1f, 0xe2, 0x52, 0xf3, 0xc6, 0x43, 0x66, 0xb3, 0x92, 0x5d,      \
35 			    0xc6, 0xe7, 0xc9, 0x59, 0x9d)
36 
37 #define UUID_2                                                                                     \
38 	BT_UUID_DECLARE_128(0x3f, 0xa4, 0x7f, 0x44, 0x2e, 0x2a, 0x43, 0x05, 0xab, 0x38, 0x07,      \
39 			    0x8d, 0x16, 0xbf, 0x99, 0xf1)
40 
41 static bool trigger_att_timeout;
42 static K_SEM_DEFINE(disconnected_sem, 0, 1);
43 
read_chrc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t buf_len,uint16_t offset)44 static ssize_t read_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
45 			 uint16_t buf_len, uint16_t offset)
46 {
47 	ssize_t read_len;
48 
49 	LOG_INF("ATT timeout will %sbe triggered", trigger_att_timeout ? "" : "not ");
50 
51 	if (trigger_att_timeout) {
52 		/* Sleep longer than ATT Timeout (section 3.3.3). */
53 		k_sleep(K_SECONDS(BT_ATT_TIMEOUT_SEC + 1));
54 	}
55 
56 	__ASSERT_NO_MSG(offset == 0);
57 	read_len = buf_len;
58 
59 	__ASSERT_NO_MSG(read_len >= 2);
60 	sys_put_le16(read_len, buf);
61 
62 	return read_len;
63 }
64 
65 static struct bt_gatt_attr attrs[] = {
66 	BT_GATT_PRIMARY_SERVICE(UUID_1),
67 	BT_GATT_CHARACTERISTIC(UUID_2, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_chrc, NULL, NULL),
68 };
69 
70 static struct bt_gatt_service svc = {
71 	.attrs = attrs,
72 	.attr_count = ARRAY_SIZE(attrs),
73 };
74 
bs_sync_all_log(char * log_msg)75 static void bs_sync_all_log(char *log_msg)
76 {
77 	/* Everyone meets here. */
78 	bt_testlib_bs_sync_all();
79 
80 	if (get_device_nbr() == 0) {
81 		LOG_WRN("Sync point: %s", log_msg);
82 	}
83 
84 	/* Everyone waits for d0 to finish logging. */
85 	bt_testlib_bs_sync_all();
86 }
87 
bt_enable_quiet(void)88 static inline void bt_enable_quiet(void)
89 {
90 	bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_ERR);
91 	bt_testlib_log_level_set("bt_id", LOG_LEVEL_ERR);
92 
93 	EXPECT_ZERO(bt_enable(NULL));
94 
95 	bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_INF);
96 	bt_testlib_log_level_set("bt_id", LOG_LEVEL_INF);
97 }
98 
peripheral_setup(enum bt_att_chan_opt bearer,bool timeout)99 static struct bt_conn *peripheral_setup(enum bt_att_chan_opt bearer, bool timeout)
100 {
101 	struct bt_conn *conn = NULL;
102 
103 	EXPECT_ZERO(bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, bt_get_name()));
104 
105 	trigger_att_timeout = timeout;
106 
107 	return conn;
108 }
109 
central_setup(enum bt_att_chan_opt bearer,bool timeout)110 static struct bt_conn *central_setup(enum bt_att_chan_opt bearer, bool timeout)
111 {
112 	bt_addr_le_t adva;
113 	struct bt_conn *conn = NULL;
114 
115 	EXPECT_ZERO(bt_testlib_scan_find_name(&adva, "peripheral"));
116 	EXPECT_ZERO(bt_testlib_connect(&adva, &conn));
117 
118 	/* Establish EATT bearers. */
119 	EXPECT_ZERO(bt_testlib_secure(conn, BT_SECURITY_L2));
120 
121 	while (bt_eatt_count(conn) == 0) {
122 		k_msleep(100);
123 	};
124 
125 	return conn;
126 }
127 
central_read(struct bt_conn * conn,enum bt_att_chan_opt bearer,bool timeout)128 static void central_read(struct bt_conn *conn, enum bt_att_chan_opt bearer, bool timeout)
129 {
130 	uint16_t actual_read_len;
131 	uint16_t remote_read_send_len;
132 	uint16_t handle = 0;
133 	int err;
134 
135 	NET_BUF_SIMPLE_DEFINE(attr_value, sizeof(remote_read_send_len));
136 
137 	err = bt_testlib_att_read_by_type_sync(&attr_value, &actual_read_len, &handle, NULL, conn,
138 					       bearer, UUID_2, BT_ATT_FIRST_ATTRIBUTE_HANDLE,
139 					       BT_ATT_LAST_ATTRIBUTE_HANDLE);
140 
141 	if (timeout) {
142 		__ASSERT(err == BT_ATT_ERR_UNLIKELY, "Unexpected error %d", err);
143 	} else {
144 		__ASSERT(!err, "Unexpected error %d", err);
145 		__ASSERT(attr_value.len >= sizeof(remote_read_send_len),
146 			 "Remote sent too little data.");
147 		remote_read_send_len = net_buf_simple_pull_le16(&attr_value);
148 		__ASSERT(remote_read_send_len == actual_read_len, "Length mismatch. %u %u",
149 			 remote_read_send_len, actual_read_len);
150 	}
151 }
152 
153 /**
154  * Test procedure:
155  *
156  * Central:
157  * 1. Connect to the peripheral.
158  * 2. Try to read a characteristic value.
159  * 3. Expect BT_ATT_ERR_UNLIKELY error.
160  * 4. Expect the peripheral to disconnect.
161  * 5. Reconnect to the peripheral.
162  * 6. Try to read a characteristic value.
163  * 7. Expect the peripheral to respond with the characteristic value.
164  * 8. Ensure that connection stays alive after a delay equal to ATT timeout.
165  * 9. Disconnect from the peripheral.
166  *
167  * Peripheral:
168  * 1. Start advertising.
169  * 2. Make the read callback sleep for more than ATT Timeout when the central tries to read.
170  * 3. Expect the disconnected callback to be called.
171  * 4. Start advertising again.
172  * 5. Make the read callback respond with the characteristic value when the central tries to read.
173  * 6. Expect the connection stay alive after a delay equal to ATT timeout.
174  * 7. Expect the central to disconnect.
175  */
test_timeout(enum bt_att_chan_opt bearer)176 static void test_timeout(enum bt_att_chan_opt bearer)
177 {
178 	bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR);
179 	bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
180 	struct bt_conn *conn;
181 	int err;
182 
183 	/* Test ATT timeout. */
184 	if (peripheral) {
185 		conn = peripheral_setup(bearer, true);
186 	}
187 
188 	if (central) {
189 		conn = central_setup(bearer, true);
190 	}
191 
192 	bs_sync_all_log("Ready to test ATT timeout");
193 
194 	if (central) {
195 		central_read(conn, bearer, true);
196 	}
197 
198 	err = k_sem_take(&disconnected_sem, K_SECONDS(BT_ATT_TIMEOUT_SEC + 2));
199 	/* Here disconnect is triggered by the Central host due to ATT timeout. */
200 	__ASSERT(!err, "Unexpected error %d", err);
201 	bt_testlib_conn_unref(&conn);
202 
203 	/* Test successful read. */
204 	if (peripheral) {
205 		conn = peripheral_setup(bearer, false);
206 	}
207 
208 	if (central) {
209 		conn = central_setup(bearer, false);
210 	}
211 
212 	bs_sync_all_log("Ready to test successful read");
213 
214 	if (central) {
215 		central_read(conn, bearer, false);
216 	}
217 
218 	err = k_sem_take(&disconnected_sem, K_SECONDS(BT_ATT_TIMEOUT_SEC + 2));
219 	/* Check that disconnect doesn't happen during time > ATT timeout. */
220 	__ASSERT(err == -EAGAIN, "Unexpected error %d", err);
221 
222 	if (central) {
223 		/* This time disconnect from the peripheral. */
224 		EXPECT_ZERO(bt_testlib_disconnect(&conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN));
225 	}
226 
227 	if (peripheral) {
228 		/* Wait for the central to disconnect. */
229 		bt_testlib_wait_disconnected(conn);
230 		bt_testlib_conn_unref(&conn);
231 	}
232 
233 	/* Clear the semaphore. */
234 	err = k_sem_take(&disconnected_sem, K_SECONDS(1));
235 	__ASSERT_NO_MSG(!err);
236 }
237 
connected(struct bt_conn * conn,uint8_t err)238 static void connected(struct bt_conn *conn, uint8_t err)
239 {
240 	LOG_INF("Connected");
241 }
242 
disconnected(struct bt_conn * conn,uint8_t reason)243 static void disconnected(struct bt_conn *conn, uint8_t reason)
244 {
245 	bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR);
246 	bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
247 	uint8_t expected_reason;
248 
249 	LOG_INF("Disconnected: %u", reason);
250 
251 	if (central) {
252 		expected_reason = BT_HCI_ERR_LOCALHOST_TERM_CONN;
253 	}
254 
255 	if (peripheral) {
256 		expected_reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN;
257 	}
258 
259 	__ASSERT(expected_reason == reason, "Unexpected reason %u", reason);
260 
261 	k_sem_give(&disconnected_sem);
262 }
263 
264 BT_CONN_CB_DEFINE(conn_callbacks) = {
265 	.connected = connected,
266 	.disconnected = disconnected,
267 };
268 
the_test(void)269 void the_test(void)
270 {
271 	bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
272 
273 	if (peripheral) {
274 		EXPECT_ZERO(bt_gatt_service_register(&svc));
275 	}
276 
277 	bt_enable_quiet();
278 
279 	if (peripheral) {
280 		EXPECT_ZERO(bt_set_name("peripheral"));
281 	}
282 
283 	bs_sync_all_log("Testing UATT");
284 	test_timeout(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
285 
286 	bs_sync_all_log("Testing EATT");
287 	test_timeout(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
288 
289 	bs_sync_all_log("Test Complete");
290 
291 	PASS("Test complete\n");
292 }
293