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(&notify_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 							       &notify_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, &notify_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