1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * EATT notification reliability test:
7 * A central acting as a GATT client scans and connects
8 * to a peripheral acting as a GATT server.
9 * The GATT client will then attempt to connect a number of CONFIG_BT_EATT_MAX bearers
10 * over EATT, send notifications, disconnect all bearers and reconnect EATT_BEARERS_TEST
11 * and send start a transaction with a request, then send a lot of notifications
12 * before the response is received.
13 * The test might be expanded by checking that all the notifications all transmitted
14 * on EATT channels.
15 */
16
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/gatt.h>
19 #include <zephyr/bluetooth/conn.h>
20 #include <zephyr/bluetooth/att.h>
21
22 #include "common.h"
23
24 CREATE_FLAG(flag_is_connected);
25 CREATE_FLAG(flag_discover_complete);
26 CREATE_FLAG(flag_is_encrypted);
27
28 static struct bt_conn *g_conn;
29 static const struct bt_gatt_attr *local_attr;
30 static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
31
32 #define NUM_NOTIF 100
33 #define SAMPLE_DATA 1
34 #define EATT_BEARERS_TEST 1
35
36 volatile int num_eatt_channels;
37
connected(struct bt_conn * conn,uint8_t err)38 static void connected(struct bt_conn *conn, uint8_t err)
39 {
40 char addr[BT_ADDR_LE_STR_LEN];
41
42 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
43
44 if (err != 0) {
45 FAIL("Failed to connect to %s (%u)\n", addr, err);
46 return;
47 }
48
49 printk("Connected to %s\n", addr);
50 SET_FLAG(flag_is_connected);
51 }
52
disconnected(struct bt_conn * conn,uint8_t reason)53 static void disconnected(struct bt_conn *conn, uint8_t reason)
54 {
55 char addr[BT_ADDR_LE_STR_LEN];
56
57 if (conn != g_conn) {
58 return;
59 }
60
61 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
62
63 printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
64
65 bt_conn_unref(g_conn);
66
67 g_conn = NULL;
68 UNSET_FLAG(flag_is_connected);
69 }
70
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err security_err)71 static void security_changed(struct bt_conn *conn, bt_security_t level,
72 enum bt_security_err security_err)
73 {
74 if (security_err == BT_SECURITY_ERR_SUCCESS && level > BT_SECURITY_L1) {
75 SET_FLAG(flag_is_encrypted);
76 }
77 }
78
79 BT_CONN_CB_DEFINE(conn_callbacks) = {
80 .connected = connected,
81 .disconnected = disconnected,
82 .security_changed = security_changed,
83 };
84
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)85 void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
86 struct net_buf_simple *ad)
87 {
88 char addr_str[BT_ADDR_LE_STR_LEN];
89 int err;
90
91 if (g_conn != NULL) {
92 return;
93 }
94
95 /* We're only interested in connectable events */
96 if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
97 return;
98 }
99
100 bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
101 printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
102
103 printk("Stopping scan\n");
104 err = bt_le_scan_stop();
105 if (err != 0) {
106 FAIL("Could not stop scan: %d");
107 return;
108 }
109
110 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
111 BT_LE_CONN_PARAM_DEFAULT, &g_conn);
112 if (err != 0) {
113 FAIL("Could not connect to peer: %d", err);
114 }
115 }
116
send_notification(void)117 void send_notification(void)
118 {
119 const uint8_t sample_dat = SAMPLE_DATA;
120 int err;
121
122 do {
123 err = bt_gatt_notify(g_conn, local_attr, &sample_dat, sizeof(sample_dat));
124 if (!err) {
125 return;
126 } else if (err != -ENOMEM) {
127 printk("GATT notify failed (err %d)\n", err);
128 return;
129 }
130 k_sleep(K_TICKS(1));
131 } while (err == -ENOMEM);
132 }
133
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)134 static uint8_t discover_func(struct bt_conn *conn,
135 const struct bt_gatt_attr *attr,
136 struct bt_gatt_discover_params *params)
137 {
138 SET_FLAG(flag_discover_complete);
139 printk("Discover complete\n");
140
141 return BT_GATT_ITER_STOP;
142 }
143
gatt_discover(void)144 static void gatt_discover(void)
145 {
146 static struct bt_gatt_discover_params discover_params;
147 int err;
148
149 printk("Discovering services and characteristics\n");
150
151 discover_params.uuid = test_svc_uuid;
152 discover_params.func = discover_func;
153 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
154 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
155 discover_params.type = BT_GATT_DISCOVER_PRIMARY;
156 discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
157
158 err = bt_gatt_discover(g_conn, &discover_params);
159 if (err != 0) {
160 FAIL("Discover failed(err %d)\n", err);
161 }
162 }
163
164 BT_GATT_SERVICE_DEFINE(g_svc,
165 BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
166 BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID, BT_GATT_CHRC_NOTIFY,
167 BT_GATT_PERM_READ, NULL, NULL, NULL),
168 BT_GATT_CCC(NULL,
169 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
170
test_main(void)171 static void test_main(void)
172 {
173 int err;
174
175 device_sync_init(PERIPHERAL_ID);
176
177 err = bt_enable(NULL);
178 if (err != 0) {
179 FAIL("Bluetooth enable failed (err %d)\n", err);
180 }
181
182 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
183 if (err != 0) {
184 FAIL("Scanning failed to start (err %d)\n", err);
185 }
186
187 printk("Scanning successfully started\n");
188
189 WAIT_FOR_FLAG(flag_is_connected);
190
191 err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
192 if (err) {
193 FAIL("Failed to start encryption procedure\n");
194 }
195
196 WAIT_FOR_FLAG(flag_is_encrypted);
197
198 err = bt_eatt_connect(g_conn, CONFIG_BT_EATT_MAX);
199 if (err) {
200 FAIL("Sending credit based connection request failed (err %d)\n", err);
201 }
202
203 /* Wait for the channels to be connected */
204 while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
205 k_sleep(K_TICKS(1));
206 }
207
208 printk("Waiting for sync\n");
209 device_sync_wait();
210
211 local_attr = &g_svc.attrs[1];
212
213 printk("############# Notification test\n");
214 for (int idx = 0; idx < NUM_NOTIF; idx++) {
215 printk("Notification %d\n", idx);
216 send_notification();
217 }
218
219 printk("############# Disconnect and reconnect\n");
220 for (int idx = 0; idx < CONFIG_BT_EATT_MAX; idx++) {
221 bt_eatt_disconnect_one(g_conn);
222 while (bt_eatt_count(g_conn) != (CONFIG_BT_EATT_MAX - idx)) {
223 k_sleep(K_TICKS(1));
224 }
225 }
226
227 printk("Connecting %d bearers\n", EATT_BEARERS_TEST);
228 err = bt_eatt_connect(g_conn, EATT_BEARERS_TEST);
229 if (err) {
230 FAIL("Sending credit based connection request failed (err %d)\n", err);
231 }
232
233 /* Wait for the channels to be connected */
234 while (bt_eatt_count(g_conn) < EATT_BEARERS_TEST) {
235 k_sleep(K_TICKS(1));
236 }
237
238 printk("############# Send notifications during discovery request\n");
239 gatt_discover();
240 while (!TEST_FLAG(flag_discover_complete)) {
241 printk("Notifying...\n");
242 send_notification();
243 }
244
245 printk("Sending final sync\n");
246 device_sync_send();
247
248 PASS("Client Passed\n");
249 }
250
251 static const struct bst_test_instance test_vcs[] = {
252 {
253 .test_id = "client",
254 .test_pre_init_f = test_init,
255 .test_tick_f = test_tick,
256 .test_main_f = test_main
257 },
258 BSTEST_END_MARKER
259 };
260
test_client_install(struct bst_test_list * tests)261 struct bst_test_list *test_client_install(struct bst_test_list *tests)
262 {
263 return bst_add_tests(tests, test_vcs);
264 }
265