1 /*
2 * Copyright (c) 2025 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stddef.h>
8 #include <errno.h>
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/types.h>
12 #include <zephyr/bluetooth/bluetooth.h>
13 #include <zephyr/bluetooth/hci.h>
14 #include <zephyr/bluetooth/conn.h>
15 #include <zephyr/bluetooth/uuid.h>
16 #include <zephyr/bluetooth/gatt.h>
17
18 #include "babblekit/testcase.h"
19 #include "babblekit/flags.h"
20 #include "common.h"
21
22 extern enum bst_result_t bst_result;
23
24 DEFINE_FLAG_STATIC(flag_is_connected);
25 DEFINE_FLAG_STATIC(flag_subscribe);
26
27 static struct bt_conn *g_conn;
28
29 #define ARRAY_ITEM(i, _) i
30 const uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
31
connected(struct bt_conn * conn,uint8_t err)32 static void connected(struct bt_conn *conn, uint8_t err)
33 {
34 char addr[BT_ADDR_LE_STR_LEN];
35
36 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
37
38 if (err != 0) {
39 TEST_FAIL("Failed to connect to %s (%u)", addr, err);
40 return;
41 }
42
43 printk("Connected to %s\n", addr);
44
45 g_conn = bt_conn_ref(conn);
46 SET_FLAG(flag_is_connected);
47 }
48
disconnected(struct bt_conn * conn,uint8_t reason)49 static void disconnected(struct bt_conn *conn, uint8_t reason)
50 {
51 char addr[BT_ADDR_LE_STR_LEN];
52
53 if (conn != g_conn) {
54 return;
55 }
56
57 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
58
59 printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
60
61 bt_conn_unref(g_conn);
62
63 g_conn = NULL;
64 UNSET_FLAG(flag_is_connected);
65 }
66
67 BT_CONN_CB_DEFINE(conn_callbacks) = {
68 .connected = connected,
69 .disconnected = disconnected,
70 };
71
read_test_chrc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)72 static ssize_t read_test_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
73 uint16_t len, uint16_t offset)
74 {
75 printk("Read char\n");
76 return bt_gatt_attr_read(conn, attr, buf, len, offset, (void *)chrc_data,
77 sizeof(chrc_data));
78 }
79
subscribe(const struct bt_gatt_attr * attr,uint16_t value)80 static void subscribe(const struct bt_gatt_attr *attr, uint16_t value)
81 {
82 const bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
83
84 if (notif_enabled) {
85 SET_FLAG(flag_subscribe);
86 }
87
88 printk("Notifications %s\n", notif_enabled ? "enabled" : "disabled");
89 }
90
91 BT_GATT_SERVICE_DEFINE(test_svc, BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
92 BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID,
93 BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_READ,
94 BT_GATT_PERM_READ, read_test_chrc, NULL, NULL),
95 BT_GATT_CCC(subscribe, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
96
97 static volatile size_t num_notifications_sent;
98
notification_sent(struct bt_conn * conn,void * user_data)99 static void notification_sent(struct bt_conn *conn, void *user_data)
100 {
101 size_t *length = user_data;
102
103 printk("Sent notification #%u with length %d\n", num_notifications_sent++, *length);
104 }
105
notify(void)106 static void notify(void)
107 {
108 static size_t length = CHRC_SIZE;
109 static struct bt_gatt_notify_params params = {
110 .attr = &attr_test_svc[1],
111 .data = chrc_data,
112 .len = CHRC_SIZE,
113 .func = notification_sent,
114 .user_data = &length,
115 .uuid = NULL,
116 .chan_opt = BT_ATT_CHAN_OPT_ENHANCED_ONLY,
117 };
118 int err;
119
120 do {
121 err = bt_gatt_notify_cb(g_conn, ¶ms);
122
123 if (err == -ENOMEM) {
124 /* The delay is needed to schedule other threads, but keep it as short as
125 * possible.
126 */
127 k_sleep(K_MSEC(1));
128 } else if (err) {
129 TEST_FAIL("Notify failed (err %d)", err);
130 }
131 } while (err);
132 }
133
setup(void)134 static void setup(void)
135 {
136 int err;
137 const struct bt_data ad[] = {
138 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
139 };
140
141 err = bt_enable(NULL);
142 if (err != 0) {
143 TEST_FAIL("Bluetooth init failed (err %d)", err);
144 return;
145 }
146
147 printk("Bluetooth initialized\n");
148
149 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
150 if (err != 0) {
151 TEST_FAIL("Advertising failed to start (err %d)", err);
152 return;
153 }
154
155 printk("Advertising successfully started\n");
156
157 WAIT_FOR_FLAG(flag_is_connected);
158
159 while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
160 k_sleep(K_MSEC(100));
161 }
162 printk("EATT connected\n");
163
164 WAIT_FOR_FLAG(flag_subscribe);
165 }
166
test_main_server(void)167 static void test_main_server(void)
168 {
169 setup();
170
171 for (int i = 0; i < NOTIFICATION_COUNT; i++) {
172 notify();
173 }
174
175 while (num_notifications_sent < NOTIFICATION_COUNT) {
176 k_sleep(K_MSEC(100));
177 }
178
179 TEST_PASS("GATT server passed");
180 }
181
182 static const struct bst_test_instance test_gatt_server[] = {
183 {
184 .test_id = "gatt_server_enhanced_notif_stress",
185 .test_main_f = test_main_server,
186 },
187 BSTEST_END_MARKER,
188 };
189
test_gatt_server_install(struct bst_test_list * tests)190 struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests)
191 {
192 return bst_add_tests(tests, test_gatt_server);
193 }
194