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