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