1 /* Copyright (c) 2023 Nordic Semiconductor ASA
2 * SPDX-License-Identifier: Apache-2.0
3 */
4
5 #include <stdint.h>
6
7 #include <zephyr/kernel.h>
8
9 #include <zephyr/bluetooth/addr.h>
10 #include <zephyr/bluetooth/conn.h>
11 #include <zephyr/bluetooth/gatt.h>
12 #include <zephyr/bluetooth/uuid.h>
13 #include <zephyr/bluetooth/bluetooth.h>
14
15 #include <zephyr/logging/log.h>
16
17 #include <zephyr/settings/settings.h>
18
19 #include "common.h"
20 #include "settings.h"
21
22 #include "argparse.h"
23 #include "babblekit/testcase.h"
24 #include "babblekit/flags.h"
25 #include "babblekit/sync.h"
26
27 #define CLIENT_CHAN 0
28
29 DEFINE_FLAG_STATIC(connected_flag);
30 DEFINE_FLAG_STATIC(disconnected_flag);
31 DEFINE_FLAG_STATIC(security_updated_flag);
32
33 DEFINE_FLAG_STATIC(ccc_cfg_changed_flag);
34
35 static const struct bt_uuid_128 dummy_service = BT_UUID_INIT_128(DUMMY_SERVICE_TYPE);
36
37 static const struct bt_uuid_128 notify_characteristic_uuid =
38 BT_UUID_INIT_128(DUMMY_SERVICE_NOTIFY_TYPE);
39
40 static struct bt_conn *default_conn;
41
42 static struct bt_conn_cb peripheral_cb;
43
ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)44 static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
45 {
46 ARG_UNUSED(attr);
47
48 bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
49
50 LOG_INF("CCC Update: notification %s", notif_enabled ? "enabled" : "disabled");
51
52 SET_FLAG(ccc_cfg_changed_flag);
53 }
54
55 BT_GATT_SERVICE_DEFINE(dummy_svc, BT_GATT_PRIMARY_SERVICE(&dummy_service),
56 BT_GATT_CHARACTERISTIC(¬ify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY,
57 BT_GATT_PERM_NONE, NULL, NULL, NULL),
58 BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
59
create_adv(struct bt_le_ext_adv ** adv)60 static void create_adv(struct bt_le_ext_adv **adv)
61 {
62 int err;
63
64 err = bt_le_ext_adv_create(BT_LE_ADV_CONN_FAST_1, NULL, adv);
65 if (err) {
66 TEST_FAIL("Failed to create advertiser (%d)", err);
67 }
68 }
69
start_adv(struct bt_le_ext_adv * adv)70 static void start_adv(struct bt_le_ext_adv *adv)
71 {
72 int err;
73 int32_t timeout = 0;
74 int8_t num_events = 0;
75
76 struct bt_le_ext_adv_start_param start_params;
77
78 start_params.timeout = timeout;
79 start_params.num_events = num_events;
80
81 err = bt_le_ext_adv_start(adv, &start_params);
82 if (err) {
83 TEST_FAIL("Failed to start advertiser (err %d)", err);
84 }
85
86 LOG_DBG("Advertiser started");
87 }
88
stop_adv(struct bt_le_ext_adv * adv)89 static void stop_adv(struct bt_le_ext_adv *adv)
90 {
91 int err;
92
93 err = bt_le_ext_adv_stop(adv);
94 if (err) {
95 TEST_FAIL("Failed to stop advertiser (err %d)", err);
96 }
97 }
98
connected(struct bt_conn * conn,uint8_t err)99 static void connected(struct bt_conn *conn, uint8_t err)
100 {
101 char addr_str[BT_ADDR_LE_STR_LEN];
102
103 bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
104
105 if (err) {
106 TEST_FAIL("Failed to connect to %s (err %d)", addr_str, err);
107 }
108
109 LOG_DBG("Connected: %s", addr_str);
110
111 default_conn = bt_conn_ref(conn);
112
113 SET_FLAG(connected_flag);
114 UNSET_FLAG(disconnected_flag);
115 }
116
disconnected(struct bt_conn * conn,uint8_t reason)117 static void disconnected(struct bt_conn *conn, uint8_t reason)
118 {
119 char addr_str[BT_ADDR_LE_STR_LEN];
120
121 bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
122
123 LOG_DBG("Disconnected: %s (reason 0x%02x)", addr_str, reason);
124
125 bt_conn_unref(conn);
126 default_conn = NULL;
127
128 SET_FLAG(disconnected_flag);
129 UNSET_FLAG(connected_flag);
130 }
131
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)132 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
133 {
134 char addr_str[BT_ADDR_LE_STR_LEN];
135
136 bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
137
138 if (!err) {
139 LOG_DBG("Security changed: %s level %u", addr_str, level);
140 SET_FLAG(security_updated_flag);
141 } else {
142 LOG_DBG("Security failed: %s level %u err %d", addr_str, level, err);
143 }
144 }
145
is_peer_subscribed(struct bt_conn * conn)146 static bool is_peer_subscribed(struct bt_conn *conn)
147 {
148 struct bt_gatt_attr *attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_DUMMY_SERVICE_NOTIFY);
149 bool is_subscribed = bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY);
150
151 return is_subscribed;
152 }
153
154 /* Test steps */
155
send_value_notification(void)156 static void send_value_notification(void)
157 {
158 const struct bt_gatt_attr *attr = bt_gatt_find_by_uuid(NULL, 0,
159 ¬ify_characteristic_uuid.uuid);
160 static uint8_t value;
161 int err;
162
163 err = bt_gatt_notify(default_conn, attr, &value, sizeof(value));
164 if (err != 0) {
165 TEST_FAIL("Failed to send notification (err %d)", err);
166 }
167
168 value++;
169 }
170
connect_pair_check_subscribtion(struct bt_le_ext_adv * adv)171 static void connect_pair_check_subscribtion(struct bt_le_ext_adv *adv)
172 {
173 start_adv(adv);
174
175 WAIT_FOR_FLAG(connected_flag);
176
177 WAIT_FOR_FLAG(security_updated_flag);
178 UNSET_FLAG(security_updated_flag);
179
180 /* wait for confirmation of subscribtion from good client */
181 bk_sync_wait();
182
183 /* check that subscribtion request did not fail */
184 if (!is_peer_subscribed(default_conn)) {
185 TEST_FAIL("Client did not subscribed");
186 }
187
188 stop_adv(adv);
189
190 /* confirm to client that the subscribtion has been well registered */
191 bk_sync_send();
192
193 send_value_notification();
194 }
195
connect_restore_sec_check_subscribtion(struct bt_le_ext_adv * adv)196 static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv)
197 {
198 start_adv(adv);
199
200 WAIT_FOR_FLAG(connected_flag);
201
202 WAIT_FOR_FLAG(security_updated_flag);
203 UNSET_FLAG(security_updated_flag);
204
205 /* wait for client end of security update */
206 bk_sync_wait();
207
208 /* check that subscribtion has been restored */
209 if (!is_peer_subscribed(default_conn)) {
210 TEST_FAIL("Client is not subscribed");
211 } else {
212 LOG_DBG("Client is subscribed");
213 }
214
215 /* confirm to good client that the subscribtion has been well restored */
216 bk_sync_send();
217
218 send_value_notification();
219 }
220
221 /* Util functions */
222
check_ccc_handle(void)223 static void check_ccc_handle(void)
224 {
225 struct bt_gatt_attr *service_notify_attr =
226 bt_gatt_find_by_uuid(NULL, 0, ¬ify_characteristic_uuid.uuid);
227
228 uint16_t actual_val_handle = bt_gatt_attr_get_handle(service_notify_attr);
229
230 __ASSERT(actual_val_handle == VAL_HANDLE,
231 "Please update the VAL_HANDLE define (actual_val_handle=%d)", actual_val_handle);
232
233 struct bt_gatt_attr *ccc_attr =
234 bt_gatt_find_by_uuid(service_notify_attr, 0, BT_UUID_GATT_CCC);
235 uint16_t actual_ccc_handle = bt_gatt_attr_get_handle(ccc_attr);
236
237 __ASSERT(actual_ccc_handle == CCC_HANDLE,
238 "Please update the CCC_HANDLE define (actual_ccc_handle=%d)", actual_ccc_handle);
239 }
240
241 /* Main function */
242
run_peripheral(int times)243 void run_peripheral(int times)
244 {
245 int err;
246 struct bt_le_ext_adv *adv = NULL;
247
248 peripheral_cb.connected = connected;
249 peripheral_cb.disconnected = disconnected;
250 peripheral_cb.security_changed = security_changed;
251
252 TEST_ASSERT(bk_sync_init() == 0);
253
254 err = bt_enable(NULL);
255 if (err) {
256 TEST_FAIL("Bluetooth init failed (err %d)", err);
257 }
258
259 LOG_DBG("Bluetooth initialized");
260
261 check_ccc_handle();
262
263 bt_conn_cb_register(&peripheral_cb);
264
265 err = settings_load();
266 if (err) {
267 TEST_FAIL("Settings load failed (err %d)", err);
268 }
269
270 err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
271 if (err) {
272 TEST_FAIL("Unpairing failed (err %d)", err);
273 }
274
275 create_adv(&adv);
276
277 connect_pair_check_subscribtion(adv);
278 WAIT_FOR_FLAG(disconnected_flag);
279
280 for (int i = 0; i < times; i++) {
281 connect_restore_sec_check_subscribtion(adv);
282 WAIT_FOR_FLAG(disconnected_flag);
283 }
284
285 TEST_PASS("Peripheral test passed");
286 }
287