1 /* Copyright (c) 2023 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 "testlib/adv.h"
14 #include "testlib/att_read.h"
15 #include "testlib/att_write.h"
16 #include "bs_macro.h"
17 #include "bs_sync.h"
18 #include <testlib/conn.h>
19 #include "testlib/log_utils.h"
20 #include "testlib/scan.h"
21 #include "testlib/security.h"
22 
23 /* This test uses system asserts to fail tests. */
24 BUILD_ASSERT(__ASSERT_ON);
25 
26 #define CENTRAL_DEVICE_NBR    0
27 #define PERIPHERAL_DEVICE_NBR 1
28 
29 LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
30 
31 #define UUID_1                                                                                     \
32 	BT_UUID_DECLARE_128(0xdb, 0x1f, 0xe2, 0x52, 0xf3, 0xc6, 0x43, 0x66, 0xb3, 0x92, 0x5d,      \
33 			    0xc6, 0xe7, 0xc9, 0x59, 0x9d)
34 
35 #define UUID_2                                                                                     \
36 	BT_UUID_DECLARE_128(0x3f, 0xa4, 0x7f, 0x44, 0x2e, 0x2a, 0x43, 0x05, 0xab, 0x38, 0x07,      \
37 			    0x8d, 0x16, 0xbf, 0x99, 0xf1)
38 
read_mtu_validation_chrc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t buf_len,uint16_t offset)39 static ssize_t read_mtu_validation_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
40 					void *buf, uint16_t buf_len, uint16_t offset)
41 {
42 
43 	LOG_DBG("Server side buf_len %u", buf_len);
44 
45 	k_msleep(100);
46 
47 	LOG_DBG("============================> trigger disconnect");
48 	bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_POWER_OFF);
49 
50 	/* We ain't read nothin' */
51 	return 0;
52 }
53 
54 static struct bt_gatt_attr attrs[] = {
55 	BT_GATT_PRIMARY_SERVICE(UUID_1),
56 	BT_GATT_CHARACTERISTIC(UUID_2, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
57 			       read_mtu_validation_chrc, NULL, NULL),
58 };
59 
60 static struct bt_gatt_service svc = {
61 	.attrs = attrs,
62 	.attr_count = ARRAY_SIZE(attrs),
63 };
64 
find_the_chrc(struct bt_conn * conn,uint16_t * chrc_value_handle)65 static void find_the_chrc(struct bt_conn *conn, uint16_t *chrc_value_handle)
66 {
67 	uint16_t svc_handle;
68 	uint16_t svc_end_handle;
69 	uint16_t chrc_end_handle;
70 
71 	EXPECT_ZERO(bt_testlib_gatt_discover_primary(&svc_handle, &svc_end_handle, conn, UUID_1, 1,
72 						     0xffff));
73 
74 	LOG_DBG("svc_handle: %u, svc_end_handle: %u", svc_handle, svc_end_handle);
75 
76 	EXPECT_ZERO(bt_testlib_gatt_discover_characteristic(chrc_value_handle, &chrc_end_handle,
77 							    NULL, conn, UUID_2, (svc_handle + 1),
78 							    svc_end_handle));
79 
80 	LOG_DBG("chrc_value_handle: %u, chrc_end_handle: %u", *chrc_value_handle, chrc_end_handle);
81 }
82 
bs_sync_all_log(char * log_msg)83 static void bs_sync_all_log(char *log_msg)
84 {
85 	/* Everyone meets here. */
86 	bt_testlib_bs_sync_all();
87 
88 	if (get_device_nbr() == 0) {
89 		LOG_WRN("Sync point: %s", log_msg);
90 	}
91 
92 	/* Everyone waits for d0 to finish logging. */
93 	bt_testlib_bs_sync_all();
94 }
95 
bt_enable_quiet(void)96 static inline void bt_enable_quiet(void)
97 {
98 	bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_ERR);
99 	bt_testlib_log_level_set("bt_id", LOG_LEVEL_ERR);
100 
101 	EXPECT_ZERO(bt_enable(NULL));
102 
103 	bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_INF);
104 	bt_testlib_log_level_set("bt_id", LOG_LEVEL_INF);
105 }
106 
107 #define ITERATIONS 20
108 #define READ_PARAMS_COUNT 20
109 static struct bt_gatt_read_params closet[READ_PARAMS_COUNT];
110 
111 static volatile int outstanding;
112 
gatt_read_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)113 static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err,
114 			    struct bt_gatt_read_params *params, const void *data,
115 			    uint16_t length)
116 {
117 	LOG_DBG("<------------------------- read done: err %d", err);
118 
119 	outstanding--;
120 
121 	return 0;
122 }
123 
a_test_iteration(int i)124 void a_test_iteration(int i)
125 {
126 	bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR);
127 	bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
128 	bt_addr_le_t adva;
129 	uint16_t chrc_value_handle = 0;
130 	struct bt_conn *conn = NULL;
131 	int err;
132 
133 	LOG_DBG("############################## start iteration %d", i);
134 
135 	bs_sync_all_log("Start iteration");
136 
137 	if (peripheral) {
138 		EXPECT_ZERO(bt_set_name("peripheral"));
139 		EXPECT_ZERO(bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, bt_get_name()));
140 	}
141 
142 	if (central) {
143 		EXPECT_ZERO(bt_testlib_scan_find_name(&adva, "peripheral"));
144 		EXPECT_ZERO(bt_testlib_connect(&adva, &conn));
145 
146 		/* Establish EATT bearers. */
147 		EXPECT_ZERO(bt_testlib_secure(conn, BT_SECURITY_L2));
148 
149 		while (bt_eatt_count(conn) == 0) {
150 			k_msleep(100);
151 		};
152 	}
153 
154 	bs_sync_all_log("Connected");
155 
156 	/* Perform discovery. */
157 	if (central) {
158 		find_the_chrc(conn, &chrc_value_handle);
159 	} else {
160 		/* Peripheral will use handle 0.
161 		 *
162 		 * This will return a permission denied from the central's
163 		 * server. It doesn't matter as the only thing we want as the
164 		 * peripheral is to also be trying to fill the TX queue with ATT
165 		 * PDUs.
166 		 */
167 	}
168 
169 	/* Test purpose: verify no allocated resource leaks when disconnecting
170 	 * abruptly with non-empty queues.
171 	 *
172 	 * Test procedure (in a nutshell):
173 	 * - open channels
174 	 * - queue up lots of ATT bufs from both sides
175 	 * - disconnect ACL
176 	 * - see if anything stalls or leaks
177 	 *
178 	 * Run this procedure more times than there are said resources.
179 	 */
180 	for (int p = 0; p < ARRAY_SIZE(closet); p++) {
181 		memset(&closet[p], 0, sizeof(struct bt_gatt_read_params));
182 
183 		closet[p].handle_count = 1;
184 		closet[p].single.handle = chrc_value_handle;
185 		closet[p].func = gatt_read_cb;
186 
187 		/* A disconnected channel (or ACL conn) can end up with
188 		 * gatt_read returning -ENOMEM instead of -ENOTCONN. sometimes.
189 		 */
190 		LOG_DBG("-------------------------> gatt_read %d", p);
191 		err = bt_gatt_read(conn, &closet[p]);
192 		switch (err) {
193 		case -ENOMEM:
194 		case -ENOTCONN:
195 			LOG_DBG("not connected");
196 			break;
197 		case 0:
198 			outstanding++;
199 			break;
200 		default:
201 			FAIL("unexpected error: %d\n", err);
202 			break;
203 		}
204 	}
205 
206 	bt_testlib_wait_disconnected(conn);
207 	bt_testlib_conn_unref(&conn);
208 
209 	k_msleep(1000);		/* beauty rest */
210 	EXPECT_ZERO(outstanding);
211 
212 	LOG_DBG("ended iteration %d", i);
213 }
214 
the_test(void)215 void the_test(void)
216 {
217 	bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
218 
219 	if (peripheral) {
220 		EXPECT_ZERO(bt_gatt_service_register(&svc));
221 	}
222 
223 	bt_enable_quiet();
224 
225 	for (int i = 0; i < ITERATIONS; i++) {
226 		a_test_iteration(i);
227 	}
228 
229 	bs_sync_all_log("Test Complete");
230 
231 	PASS("Test complete\n");
232 }
233