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 "bs_pc_backchannel.h"
24 
25 #define GOOD_CLIENT_CHAN 0
26 #define BAD_CLIENT_CHAN 1
27 
28 CREATE_FLAG(connected_flag);
29 CREATE_FLAG(disconnected_flag);
30 CREATE_FLAG(security_updated_flag);
31 
32 CREATE_FLAG(ccc_cfg_changed_flag);
33 
34 static const struct bt_uuid_128 dummy_service = BT_UUID_INIT_128(DUMMY_SERVICE_TYPE);
35 
36 static const struct bt_uuid_128 notify_characteristic_uuid =
37 					BT_UUID_INIT_128(DUMMY_SERVICE_NOTIFY_TYPE);
38 
39 static struct bt_conn *default_conn;
40 
41 static struct bt_conn_cb peripheral_cb;
42 
ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)43 static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
44 {
45 	ARG_UNUSED(attr);
46 
47 	bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
48 
49 	LOG_INF("CCC Update: notification %s", notif_enabled ? "enabled" : "disabled");
50 
51 	SET_FLAG(ccc_cfg_changed_flag);
52 }
53 
54 BT_GATT_SERVICE_DEFINE(dummy_svc, BT_GATT_PRIMARY_SERVICE(&dummy_service),
55 		       BT_GATT_CHARACTERISTIC(&notify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY,
56 					      BT_GATT_PERM_NONE, NULL, NULL, NULL),
57 		       BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
58 
create_adv(struct bt_le_ext_adv ** adv)59 static void create_adv(struct bt_le_ext_adv **adv)
60 {
61 	int err;
62 	struct bt_le_adv_param params;
63 
64 	memset(&params, 0, sizeof(struct bt_le_adv_param));
65 
66 	params.options |= BT_LE_ADV_OPT_CONN;
67 
68 	params.id = BT_ID_DEFAULT;
69 	params.sid = 0;
70 	params.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
71 	params.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;
72 
73 	err = bt_le_ext_adv_create(&params, NULL, adv);
74 	if (err) {
75 		FAIL("Failed to create advertiser (%d)\n", err);
76 	}
77 }
78 
start_adv(struct bt_le_ext_adv * adv)79 static void start_adv(struct bt_le_ext_adv *adv)
80 {
81 	int err;
82 	int32_t timeout = 0;
83 	int8_t num_events = 0;
84 
85 	struct bt_le_ext_adv_start_param start_params;
86 
87 	start_params.timeout = timeout;
88 	start_params.num_events = num_events;
89 
90 	err = bt_le_ext_adv_start(adv, &start_params);
91 	if (err) {
92 		FAIL("Failed to start advertiser (err %d)\n", err);
93 	}
94 
95 	LOG_DBG("Advertiser started");
96 }
97 
stop_adv(struct bt_le_ext_adv * adv)98 static void stop_adv(struct bt_le_ext_adv *adv)
99 {
100 	int err;
101 
102 	err = bt_le_ext_adv_stop(adv);
103 	if (err) {
104 		FAIL("Failed to stop advertiser (err %d)\n", err);
105 	}
106 }
107 
connected(struct bt_conn * conn,uint8_t err)108 static void connected(struct bt_conn *conn, uint8_t err)
109 {
110 	char addr_str[BT_ADDR_LE_STR_LEN];
111 
112 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
113 
114 	if (err) {
115 		FAIL("Failed to connect to %s (err %d)\n", addr_str, err);
116 	}
117 
118 	LOG_DBG("Connected: %s", addr_str);
119 
120 	default_conn = bt_conn_ref(conn);
121 
122 	SET_FLAG(connected_flag);
123 	UNSET_FLAG(disconnected_flag);
124 }
125 
disconnected(struct bt_conn * conn,uint8_t reason)126 static void disconnected(struct bt_conn *conn, uint8_t reason)
127 {
128 	char addr_str[BT_ADDR_LE_STR_LEN];
129 
130 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
131 
132 	LOG_DBG("Disconnected: %s (reason 0x%02x)", addr_str, reason);
133 
134 	bt_conn_unref(conn);
135 	default_conn = NULL;
136 
137 	SET_FLAG(disconnected_flag);
138 	UNSET_FLAG(connected_flag);
139 }
140 
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)141 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
142 {
143 	char addr_str[BT_ADDR_LE_STR_LEN];
144 
145 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
146 
147 	if (!err) {
148 		LOG_DBG("Security changed: %s level %u", addr_str, level);
149 		SET_FLAG(security_updated_flag);
150 	} else {
151 		LOG_DBG("Security failed: %s level %u err %d", addr_str, level, err);
152 	}
153 }
154 
is_peer_subscribed(struct bt_conn * conn)155 static bool is_peer_subscribed(struct bt_conn *conn)
156 {
157 	struct bt_gatt_attr *attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_DUMMY_SERVICE_NOTIFY);
158 	bool is_subscribed = bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY);
159 
160 	return is_subscribed;
161 }
162 
163 /* Test steps */
164 
connect_pair_check_subscribtion(struct bt_le_ext_adv * adv)165 static void connect_pair_check_subscribtion(struct bt_le_ext_adv *adv)
166 {
167 	start_adv(adv);
168 
169 	WAIT_FOR_FLAG(connected_flag);
170 
171 	WAIT_FOR_FLAG(security_updated_flag);
172 	UNSET_FLAG(security_updated_flag);
173 
174 	/* wait for confirmation of subscribtion from good client */
175 	backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
176 
177 	/* check that subscribtion request did not fail */
178 	if (!is_peer_subscribed(default_conn)) {
179 		FAIL("Good client did not subscribed\n");
180 	}
181 
182 	stop_adv(adv);
183 
184 	/* confirm to good client that the subscribtion has been well registered */
185 	backchannel_sync_send(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
186 }
187 
connect_wait_unsubscribtion(struct bt_le_ext_adv * adv)188 static void connect_wait_unsubscribtion(struct bt_le_ext_adv *adv)
189 {
190 	UNSET_FLAG(ccc_cfg_changed_flag);
191 
192 	start_adv(adv);
193 
194 	WAIT_FOR_FLAG(connected_flag);
195 
196 	stop_adv(adv);
197 
198 	/* check that subscribtion is restored for bad client */
199 	if (!is_peer_subscribed(default_conn)) {
200 		FAIL("Subscribtion has not been restored for bad client\n");
201 	}
202 
203 	/* confirm to bad client that the subscribtion had not been restored */
204 	backchannel_sync_send(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
205 	/* wait for confirmation that bad client requested unsubscribtion */
206 	backchannel_sync_wait(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
207 
208 	/* check that unsubscribtion request didn't fail */
209 	if (!GET_FLAG(ccc_cfg_changed_flag)) {
210 		FAIL("Bad client didn't manage to update CCC config\n");
211 	}
212 
213 	/* confirm to bad client that unsubscribtion request has been well registered */
214 	backchannel_sync_send(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
215 }
216 
connect_restore_sec_check_subscribtion(struct bt_le_ext_adv * adv)217 static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv)
218 {
219 	start_adv(adv);
220 
221 	WAIT_FOR_FLAG(connected_flag);
222 
223 	WAIT_FOR_FLAG(security_updated_flag);
224 	UNSET_FLAG(security_updated_flag);
225 
226 	/* wait for good client end of security update */
227 	backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
228 
229 	/* check that subscribtion hasn't been restored */
230 	if (is_peer_subscribed(default_conn)) {
231 		FAIL("Good client is subscribed\n");
232 	}
233 
234 	/* confirm to good client that the subscribtion has been well restored */
235 	backchannel_sync_send(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
236 	/* wait for confimation of unsubscribtion from good client */
237 	backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
238 
239 	/* check that unsubscribtion request from good client has been registered */
240 	if (is_peer_subscribed(default_conn)) {
241 		FAIL("Good client did not unsubscribe\n");
242 	}
243 }
244 
245 /* Util functions */
246 
peripheral_backchannel_init(void)247 void peripheral_backchannel_init(void)
248 {
249 	uint device_number = get_device_nbr();
250 	uint channel_numbers[2] = {
251 		0,
252 		0,
253 	};
254 	uint device_numbers[2] = {
255 		GOOD_CLIENT_ID,
256 		BAD_CLIENT_ID,
257 	};
258 	uint num_ch = 2;
259 	uint *ch;
260 
261 	LOG_DBG("Opening back channels for device %d", device_number);
262 	ch = bs_open_back_channel(device_number, device_numbers, channel_numbers, num_ch);
263 	if (!ch) {
264 		FAIL("Unable to open backchannel\n");
265 	}
266 }
267 
check_ccc_handle(void)268 static void check_ccc_handle(void)
269 {
270 	struct bt_gatt_attr *service_notify_attr =
271 		bt_gatt_find_by_uuid(NULL, 0, &notify_characteristic_uuid.uuid);
272 
273 	struct bt_gatt_attr attr = {
274 		.uuid = BT_UUID_GATT_CHRC,
275 		.user_data = &(struct bt_gatt_chrc){
276 			.value_handle = bt_gatt_attr_get_handle(service_notify_attr)}};
277 
278 	struct bt_gatt_attr *ccc_attr = bt_gatt_find_by_uuid(&attr, 0, BT_UUID_GATT_CCC);
279 	uint16_t actual_ccc_handle = bt_gatt_attr_get_handle(ccc_attr);
280 
281 	__ASSERT(actual_ccc_handle == CCC_HANDLE,
282 		 "Please update the CCC_HANDLE define (actual_ccc_handle=%d)", actual_ccc_handle);
283 }
284 
285 /* Main function */
286 
run_peripheral(void)287 void run_peripheral(void)
288 {
289 	/*
290 	 * test goal: demonstrate the expected behavior of the GATT server when
291 	 * a non-bonded peer try to unsubscribe from a previously subscription
292 	 * done in a bonded context
293 	 *
294 	 * test pass if the bad client manage to unsubscribe
295 	 */
296 
297 	int err;
298 	struct bt_le_ext_adv *adv = NULL;
299 
300 	peripheral_cb.connected = connected;
301 	peripheral_cb.disconnected = disconnected;
302 	peripheral_cb.security_changed = security_changed;
303 
304 	peripheral_backchannel_init();
305 
306 	err = bt_enable(NULL);
307 	if (err) {
308 		FAIL("Bluetooth init failed (err %d)\n", err);
309 	}
310 
311 	LOG_DBG("Bluetooth initialized");
312 
313 	check_ccc_handle();
314 
315 	bt_conn_cb_register(&peripheral_cb);
316 
317 	err = settings_load();
318 	if (err) {
319 		FAIL("Settings load failed (err %d)\n", err);
320 	}
321 
322 	err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
323 	if (err) {
324 		FAIL("Unpairing failed (err %d)\n", err);
325 	}
326 
327 	create_adv(&adv);
328 
329 	connect_pair_check_subscribtion(adv);
330 	WAIT_FOR_FLAG(disconnected_flag);
331 
332 	connect_wait_unsubscribtion(adv);
333 	WAIT_FOR_FLAG(disconnected_flag);
334 
335 	connect_restore_sec_check_subscribtion(adv);
336 	WAIT_FOR_FLAG(disconnected_flag);
337 
338 	PASS("Peripheral test passed\n");
339 }
340