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