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