1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdio.h>
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/byteorder.h>
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/hci.h>
13 #include <zephyr/bluetooth/l2cap.h>
14 #include <zephyr/bluetooth/att.h>
15 #include <zephyr/bluetooth/gatt.h>
16 #include <zephyr/bluetooth/uuid.h>
17 #include <zephyr/bluetooth/conn.h>
18
19 #include "utils.h"
20 #include "bstests.h"
21
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(dut, LOG_LEVEL_INF);
24
25 DEFINE_FLAG(is_connected);
26 DEFINE_FLAG(is_subscribed);
27 DEFINE_FLAG(one_indication);
28 DEFINE_FLAG(two_notifications);
29 DEFINE_FLAG(flag_data_length_updated);
30
31 static atomic_t nwrites;
32 static atomic_t indications;
33 static atomic_t notifications;
34
35 /* Defined in conn.c */
36 extern void bt_conn_suspend_tx(bool suspend);
37
38 static struct bt_conn *dconn;
39
connected(struct bt_conn * conn,uint8_t conn_err)40 static void connected(struct bt_conn *conn, uint8_t conn_err)
41 {
42 char addr[BT_ADDR_LE_STR_LEN];
43
44 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
45
46 if (conn_err) {
47 FAIL("Failed to connect to %s (%u)", addr, conn_err);
48 return;
49 }
50
51 LOG_DBG("%s", addr);
52
53 dconn = bt_conn_ref(conn);
54 SET_FLAG(is_connected);
55 }
56
disconnected(struct bt_conn * conn,uint8_t reason)57 static void disconnected(struct bt_conn *conn, uint8_t reason)
58 {
59 char addr[BT_ADDR_LE_STR_LEN];
60
61 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
62
63 LOG_DBG("%p %s (reason 0x%02x)", conn, addr, reason);
64
65 bt_conn_unref(dconn);
66 UNSET_FLAG(is_connected);
67 }
68
data_len_updated(struct bt_conn * conn,struct bt_conn_le_data_len_info * info)69 static void data_len_updated(struct bt_conn *conn,
70 struct bt_conn_le_data_len_info *info)
71 {
72 LOG_DBG("Data length updated: TX %d RX %d",
73 info->tx_max_len,
74 info->rx_max_len);
75 SET_FLAG(flag_data_length_updated);
76 }
77
do_dlu(void)78 static void do_dlu(void)
79 {
80 int err;
81 struct bt_conn_le_data_len_param param;
82
83 param.tx_max_len = CONFIG_BT_CTLR_DATA_LENGTH_MAX;
84 param.tx_max_time = 2500;
85
86 err = bt_conn_le_data_len_update(dconn, ¶m);
87 ASSERT(err == 0, "Can't update data length (err %d)\n", err);
88
89 WAIT_FOR_FLAG(flag_data_length_updated);
90 }
91
92 BT_CONN_CB_DEFINE(conn_callbacks) = {
93 .connected = connected,
94 .disconnected = disconnected,
95 .le_data_len_updated = data_len_updated,
96 };
97
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)98 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
99 struct net_buf_simple *ad)
100 {
101 char str[BT_ADDR_LE_STR_LEN];
102 struct bt_le_conn_param *param;
103 struct bt_conn *conn;
104 int err;
105
106 err = bt_le_scan_stop();
107 if (err) {
108 FAIL("Stop LE scan failed (err %d)", err);
109 return;
110 }
111
112 bt_addr_le_to_str(addr, str, sizeof(str));
113 LOG_DBG("Connecting to %s", str);
114
115 param = BT_LE_CONN_PARAM_DEFAULT;
116 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &conn);
117 if (err) {
118 FAIL("Create conn failed (err %d)", err);
119 return;
120 }
121 }
122
connect(void)123 static void connect(void)
124 {
125 int err;
126 struct bt_le_scan_param scan_param = {
127 .type = BT_LE_SCAN_TYPE_ACTIVE,
128 .options = BT_LE_SCAN_OPT_NONE,
129 .interval = BT_GAP_SCAN_FAST_INTERVAL,
130 .window = BT_GAP_SCAN_FAST_WINDOW,
131 };
132
133 UNSET_FLAG(is_connected);
134
135 err = bt_le_scan_start(&scan_param, device_found);
136 ASSERT(!err, "Scanning failed to start (err %d)\n", err);
137
138 LOG_DBG("Central initiating connection...");
139 WAIT_FOR_FLAG(is_connected);
140 LOG_INF("Connected as central");
141
142 /* No security support on the tinyhost unfortunately */
143 }
144
written_to(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)145 static ssize_t written_to(struct bt_conn *conn,
146 const struct bt_gatt_attr *attr,
147 const void *buf,
148 uint16_t len,
149 uint16_t offset,
150 uint8_t flags)
151 {
152 LOG_INF("written to: handle 0x%x len %d flags 0x%x",
153 attr->handle,
154 len,
155 flags);
156
157 LOG_HEXDUMP_DBG(buf, len, "Write data");
158
159 if (atomic_get(&nwrites) == 0) {
160 /* Suspend on the first write, which is an ATT Request */
161 LOG_INF("suspending HCI TX thread");
162 bt_conn_suspend_tx(true);
163 }
164
165 atomic_inc(&nwrites);
166
167 return len;
168 }
169
170 #define test_service_uuid \
171 BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xf0debc9a, 0x7856, 0x3412, 0x7856, 0x341278563412))
172 #define test_characteristic_uuid \
173 BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xf2debc9a, 0x7856, 0x3412, 0x7856, 0x341278563412))
174
175 BT_GATT_SERVICE_DEFINE(test_gatt_service, BT_GATT_PRIMARY_SERVICE(test_service_uuid),
176 BT_GATT_CHARACTERISTIC(test_characteristic_uuid,
177 (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
178 BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE),
179 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
180 NULL, written_to, NULL),
181 BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),);
182
notified(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)183 static uint8_t notified(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
184 const void *data, uint16_t length)
185 {
186 static uint8_t notification[] = NOTIFICATION_PAYLOAD;
187 static uint8_t indication[] = INDICATION_PAYLOAD;
188 bool is_nfy;
189
190 ASSERT(length >= sizeof(indication), "Unexpected data");
191 ASSERT(length <= sizeof(notification), "Unexpected data");
192
193 LOG_HEXDUMP_DBG(data, length, "HVx data");
194
195 is_nfy = memcmp(data, notification, length) == 0;
196
197 LOG_INF("%s from 0x%x", is_nfy ? "notified" : "indicated",
198 params->value_handle);
199
200 if (is_nfy) {
201 atomic_inc(¬ifications);
202 } else {
203 atomic_inc(&indications);
204 }
205
206 return BT_GATT_ITER_CONTINUE;
207 }
208
subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)209 static void subscribed(struct bt_conn *conn,
210 uint8_t err,
211 struct bt_gatt_subscribe_params *params)
212 {
213 ASSERT(!err, "Subscribe failed (err %d)\n", err);
214
215 ASSERT(params, "params is NULL\n");
216
217 SET_FLAG(is_subscribed);
218 /* spoiler: tester doesn't really have attributes */
219 LOG_INF("Subscribed to Tester attribute");
220 }
221
subscribe(void)222 void subscribe(void)
223 {
224 int err;
225
226 /* Handle values don't matter, as long as they match on the tester */
227 static struct bt_gatt_subscribe_params params = {
228 .notify = notified,
229 .subscribe = subscribed,
230 .value = BT_GATT_CCC_NOTIFY | BT_GATT_CCC_INDICATE,
231 .value_handle = HVX_HANDLE,
232 .ccc_handle = (HVX_HANDLE + 1),
233 };
234
235 err = bt_gatt_subscribe(dconn, ¶ms);
236 ASSERT(!err, "Subscribe failed (err %d)\n", err);
237
238 WAIT_FOR_FLAG(is_subscribed);
239 }
240
send_write_handle(void)241 static void send_write_handle(void)
242 {
243 int err;
244 uint16_t handle;
245 uint8_t data[sizeof(handle)];
246 const struct bt_gatt_attr *attr = &test_gatt_service.attrs[2];
247
248 /* Inform tester which handle it should write to */
249 handle = bt_gatt_attr_get_handle(attr);
250 sys_put_le16(handle, data);
251
252 err = bt_gatt_notify(dconn, attr, data, sizeof(data));
253 ASSERT(!err, "Failed to transmit handle for write (err %d)\n", err);
254 }
255
test_procedure_0(void)256 void test_procedure_0(void)
257 {
258 LOG_DBG("Test start: ATT sequential protocol");
259 int err;
260
261 err = bt_enable(NULL);
262 ASSERT(err == 0, "Can't enable Bluetooth (err %d)\n", err);
263 LOG_DBG("Central: Bluetooth initialized.");
264
265 /* Test purpose:
266 * Test Spec V.3 P.F 3.3.2 Sequential protocol
267 *
268 * Verify that a Zephyr host server/client combo can process
269 * concurrently: one Request, one Indication, multiple
270 * Notifications and multiple Commands.
271 *
272 * To do this, the application on the DUT will purposefully stall the
273 * HCI TX thread, ensuring that the responses are not sent until the
274 * tester has finished sending everything.
275 *
276 * Test procedure:
277 *
278 * [setup]
279 * - connect ACL
280 * - update data length (tinyhost doens't have recombination)
281 * - dut: subscribe to INDICATE and NOTIFY on tester CHRC
282 * - dut: send a handle the tester can write to
283 *
284 * [proc]
285 * - tester: send one ATT write request
286 * - tester: send one ATT indication
287 * - tester: send two ATT notifications
288 * - tester: send two ATT commands
289 *
290 * - dut: handle the REQuest, build & put the RSP PDU on the HCI TX queue
291 * - dut: suspend the HCI TX thread
292 * - dut: handle the INDication
293 * - dut: handle the notifications
294 * - dut: handle the (write) commands
295 * - dut: resume the TX thread after a short while
296 *
297 * [verdict]
298 * - all procedures complete successfully, no buffer allocation failures
299 * or timeouts.
300 */
301 connect();
302 subscribe();
303
304 do_dlu();
305
306 send_write_handle();
307
308 WAIT_FOR_VAL(indications, 1);
309 WAIT_FOR_VAL(notifications, 2);
310 /* One REQ, two CMDs */
311 WAIT_FOR_VAL(nwrites, 3);
312
313 /* Send RSP to LL */
314 bt_conn_suspend_tx(false);
315
316 PASS("DUT done\n");
317 }
318
test_tick(bs_time_t HW_device_time)319 void test_tick(bs_time_t HW_device_time)
320 {
321 bs_trace_debug_time(0, "Simulation ends now.\n");
322 if (bst_result != Passed) {
323 bst_result = Failed;
324 bs_trace_error("Test did not pass before simulation ended.\n");
325 }
326 }
327
test_init(void)328 void test_init(void)
329 {
330 bst_ticker_set_next_tick_absolute(TEST_TIMEOUT_SIMULATED);
331 bst_result = In_progress;
332 }
333
334 static const struct bst_test_instance test_to_add[] = {
335 {
336 .test_id = "dut",
337 .test_pre_init_f = test_init,
338 .test_tick_f = test_tick,
339 .test_main_f = test_procedure_0,
340 },
341 BSTEST_END_MARKER,
342 };
343
install(struct bst_test_list * tests)344 static struct bst_test_list *install(struct bst_test_list *tests)
345 {
346 return bst_add_tests(tests, test_to_add);
347 };
348
349 bst_test_install_t test_installers[] = {install, NULL};
350
main(void)351 int main(void)
352 {
353 bst_main();
354
355 return 0;
356 }
357