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, &params);
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