1 /*
2  * Copyright (c) 2021-2024 Nordic Semiconductor ASA
3  *
4  *  SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <zephyr/kernel.h>
10 
11 #include <zephyr/sys/printk.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/sys/util.h>
14 
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/gap.h>
17 #include <zephyr/bluetooth/direction.h>
18 #include <zephyr/bluetooth/hci.h>
19 #include <zephyr/bluetooth/hci_vs.h>
20 
21 #define DEVICE_NAME     CONFIG_BT_DEVICE_NAME
22 #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
23 #define NAME_LEN        30
24 #define PEER_NAME_LEN_MAX 30
25 /* BT Core 5.3 Vol 6, Part B section 4.4.5.1 Periodic Advertising Trains allows controller to wait
26  * 6 periodic advertising events for synchronization establishment, hence timeout must be longer
27  * than that.
28  */
29 #define SYNC_CREATE_TIMEOUT_INTERVAL_NUM 7
30 /* Maximum length of advertising data represented in hexadecimal format */
31 #define ADV_DATA_HEX_STR_LEN_MAX (BT_GAP_ADV_MAX_EXT_ADV_DATA_LEN * 2 + 1)
32 
33 static struct bt_le_per_adv_sync_param sync_create_param;
34 static struct bt_le_per_adv_sync *adv_sync;
35 static bt_addr_le_t per_addr;
36 static bool per_adv_found;
37 static bool scan_enabled;
38 static bool sync_wait;
39 static bool sync_terminated;
40 static uint8_t per_sid;
41 static uint16_t sync_create_timeout;
42 
43 static K_SEM_DEFINE(sem_per_adv, 0, 1);
44 static K_SEM_DEFINE(sem_per_sync, 0, 1);
45 static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
46 
47 #if defined(CONFIG_BT_DF_CTE_RX_AOA)
48 const static uint8_t ant_patterns[] = { 0x1, 0x2, 0x3, 0x4, 0x5,
49 					0x6, 0x7, 0x8, 0x9, 0xA };
50 #endif /* CONFIG_BT_DF_CTE_RX_AOA */
51 
52 static bool data_cb(struct bt_data *data, void *user_data);
53 static void create_sync(void);
54 static void scan_recv(const struct bt_le_scan_recv_info *info,
55 		      struct net_buf_simple *buf);
56 
57 static void sync_cb(struct bt_le_per_adv_sync *sync,
58 		    struct bt_le_per_adv_sync_synced_info *info);
59 static void term_cb(struct bt_le_per_adv_sync *sync,
60 		    const struct bt_le_per_adv_sync_term_info *info);
61 static void recv_cb(struct bt_le_per_adv_sync *sync,
62 		    const struct bt_le_per_adv_sync_recv_info *info,
63 		    struct net_buf_simple *buf);
64 static void scan_recv(const struct bt_le_scan_recv_info *info,
65 		      struct net_buf_simple *buf);
66 static void scan_disable(void);
67 static void cte_recv_cb(struct bt_le_per_adv_sync *sync,
68 			struct bt_df_per_adv_sync_iq_samples_report const *report);
69 
70 static struct bt_le_per_adv_sync_cb sync_callbacks = {
71 	.synced = sync_cb,
72 	.term = term_cb,
73 	.recv = recv_cb,
74 	.cte_report_cb = cte_recv_cb,
75 };
76 
77 static struct bt_le_scan_cb scan_callbacks = {
78 	.recv = scan_recv,
79 };
80 
sync_create_timeout_get(uint16_t interval)81 static uint16_t sync_create_timeout_get(uint16_t interval)
82 {
83 	uint32_t interval_us;
84 	uint32_t timeout;
85 
86 	/* Add retries and convert to unit in 10's of ms */
87 	interval_us = BT_GAP_PER_ADV_INTERVAL_TO_US(interval);
88 	timeout = BT_GAP_US_TO_PER_ADV_SYNC_TIMEOUT(interval_us) * SYNC_CREATE_TIMEOUT_INTERVAL_NUM;
89 
90 	/* Enforce restraints */
91 	timeout = CLAMP(timeout, BT_GAP_PER_ADV_MIN_TIMEOUT, BT_GAP_PER_ADV_MAX_TIMEOUT);
92 
93 	return (uint16_t)timeout;
94 }
95 
phy2str(uint8_t phy)96 static const char *phy2str(uint8_t phy)
97 {
98 	switch (phy) {
99 	case 0: return "No packets";
100 	case BT_GAP_LE_PHY_1M: return "LE 1M";
101 	case BT_GAP_LE_PHY_2M: return "LE 2M";
102 	case BT_GAP_LE_PHY_CODED: return "LE Coded";
103 	default: return "Unknown";
104 	}
105 }
106 
cte_type2str(uint8_t type)107 static const char *cte_type2str(uint8_t type)
108 {
109 	switch (type) {
110 	case BT_DF_CTE_TYPE_AOA: return "AOA";
111 	case BT_DF_CTE_TYPE_AOD_1US: return "AOD 1 [us]";
112 	case BT_DF_CTE_TYPE_AOD_2US: return "AOD 2 [us]";
113 	case BT_DF_CTE_TYPE_NONE: return "";
114 	default: return "Unknown";
115 	}
116 }
117 
sample_type2str(enum bt_df_iq_sample type)118 static const char *sample_type2str(enum bt_df_iq_sample type)
119 {
120 	switch (type) {
121 	case BT_DF_IQ_SAMPLE_8_BITS_INT:
122 		return "8 bits int";
123 	case BT_DF_IQ_SAMPLE_16_BITS_INT:
124 		return "16 bits int";
125 	default:
126 		return "Unknown";
127 	}
128 }
129 
pocket_status2str(uint8_t status)130 static const char *pocket_status2str(uint8_t status)
131 {
132 	switch (status) {
133 	case BT_DF_CTE_CRC_OK: return "CRC OK";
134 	case BT_DF_CTE_CRC_ERR_CTE_BASED_TIME: return "CRC not OK, CTE Info OK";
135 	case BT_DF_CTE_CRC_ERR_CTE_BASED_OTHER: return "CRC not OK, Sampled other way";
136 	case BT_DF_CTE_INSUFFICIENT_RESOURCES: return "No resources";
137 	default: return "Unknown";
138 	}
139 }
140 
data_cb(struct bt_data * data,void * user_data)141 static bool data_cb(struct bt_data *data, void *user_data)
142 {
143 	char *name = user_data;
144 	uint8_t len;
145 
146 	switch (data->type) {
147 	case BT_DATA_NAME_SHORTENED:
148 	case BT_DATA_NAME_COMPLETE:
149 		len = MIN(data->data_len, NAME_LEN - 1);
150 		memcpy(name, data->data, len);
151 		name[len] = '\0';
152 		return false;
153 	default:
154 		return true;
155 	}
156 }
157 
sync_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)158 static void sync_cb(struct bt_le_per_adv_sync *sync,
159 		    struct bt_le_per_adv_sync_synced_info *info)
160 {
161 	char le_addr[BT_ADDR_LE_STR_LEN];
162 
163 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
164 
165 	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
166 	       "Interval 0x%04x (%u ms), PHY %s\n",
167 	       bt_le_per_adv_sync_get_index(sync), le_addr,
168 	       info->interval, info->interval * 5 / 4, phy2str(info->phy));
169 
170 	k_sem_give(&sem_per_sync);
171 }
172 
term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)173 static void term_cb(struct bt_le_per_adv_sync *sync,
174 		    const struct bt_le_per_adv_sync_term_info *info)
175 {
176 	char le_addr[BT_ADDR_LE_STR_LEN];
177 
178 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
179 
180 	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
181 	       bt_le_per_adv_sync_get_index(sync), le_addr);
182 
183 	if (sync_wait) {
184 		sync_terminated = true;
185 		k_sem_give(&sem_per_sync);
186 	} else {
187 		k_sem_give(&sem_per_sync_lost);
188 	}
189 }
190 
recv_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)191 static void recv_cb(struct bt_le_per_adv_sync *sync,
192 		    const struct bt_le_per_adv_sync_recv_info *info,
193 		    struct net_buf_simple *buf)
194 {
195 	static char data_str[ADV_DATA_HEX_STR_LEN_MAX];
196 	char le_addr[BT_ADDR_LE_STR_LEN];
197 
198 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
199 	bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
200 
201 	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
202 	       "RSSI %i, CTE %s, data length %u, data: %s\n",
203 	       bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
204 	       info->rssi, cte_type2str(info->cte_type), buf->len, data_str);
205 }
206 
cte_recv_cb(struct bt_le_per_adv_sync * sync,struct bt_df_per_adv_sync_iq_samples_report const * report)207 static void cte_recv_cb(struct bt_le_per_adv_sync *sync,
208 			struct bt_df_per_adv_sync_iq_samples_report const *report)
209 {
210 	printk("CTE[%u]: samples type: %s, samples count %d, cte type %s, slot durations: %u [us], "
211 	       "packet status %s, RSSI %i\n",
212 	       bt_le_per_adv_sync_get_index(sync), sample_type2str(report->sample_type),
213 	       report->sample_count, cte_type2str(report->cte_type), report->slot_durations,
214 	       pocket_status2str(report->packet_status), report->rssi);
215 
216 	if (IS_ENABLED(CONFIG_DF_LOCATOR_APP_IQ_REPORT_PRINT_IQ_SAMPLES)) {
217 		for (uint8_t idx = 0; idx < report->sample_count; idx++) {
218 			if (report->sample_type == BT_DF_IQ_SAMPLE_8_BITS_INT) {
219 				printk(" IQ[%d]: %d, %d\n", idx, report->sample[idx].i,
220 				       report->sample[idx].q);
221 			} else if (IS_ENABLED(CONFIG_BT_DF_VS_CL_IQ_REPORT_16_BITS_IQ_SAMPLES)) {
222 				printk(" IQ[%" PRIu8 "]: %d, %d\n", idx, report->sample16[idx].i,
223 				       report->sample16[idx].q);
224 			} else {
225 				printk("Unhandled vendor specific IQ samples type\n");
226 				break;
227 			}
228 		}
229 	}
230 }
231 
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)232 static void scan_recv(const struct bt_le_scan_recv_info *info,
233 		      struct net_buf_simple *buf)
234 {
235 	char le_addr[BT_ADDR_LE_STR_LEN];
236 	char name[NAME_LEN];
237 
238 	(void)memset(name, 0, sizeof(name));
239 
240 	bt_data_parse(buf, data_cb, name);
241 
242 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
243 
244 	printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s C:%u S:%u "
245 	       "D:%u SR:%u E:%u Prim: %s, Secn: %s, Interval: 0x%04x (%u ms), "
246 	       "SID: %u\n",
247 	       le_addr, info->adv_type, info->tx_power, info->rssi, name,
248 	       (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
249 	       (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
250 	       (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
251 	       (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
252 	       (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0,
253 	       phy2str(info->primary_phy), phy2str(info->secondary_phy),
254 	       info->interval, info->interval * 5 / 4, info->sid);
255 
256 	if (!per_adv_found && info->interval != 0) {
257 		sync_create_timeout = sync_create_timeout_get(info->interval);
258 		per_adv_found = true;
259 		per_sid = info->sid;
260 		bt_addr_le_copy(&per_addr, info->addr);
261 
262 		k_sem_give(&sem_per_adv);
263 	}
264 }
265 
create_sync(void)266 static void create_sync(void)
267 {
268 	int err;
269 
270 	printk("Creating Periodic Advertising Sync...");
271 	bt_addr_le_copy(&sync_create_param.addr, &per_addr);
272 
273 	sync_create_param.options = BT_LE_PER_ADV_SYNC_OPT_SYNC_ONLY_CONST_TONE_EXT;
274 	sync_create_param.sid = per_sid;
275 	sync_create_param.skip = 0;
276 	sync_create_param.timeout = sync_create_timeout;
277 	err = bt_le_per_adv_sync_create(&sync_create_param, &adv_sync);
278 	if (err != 0) {
279 		printk("failed (err %d)\n", err);
280 		return;
281 	}
282 	printk("success.\n");
283 }
284 
delete_sync(void)285 static int delete_sync(void)
286 {
287 	int err;
288 
289 	printk("Deleting Periodic Advertising Sync...");
290 	err = bt_le_per_adv_sync_delete(adv_sync);
291 	if (err != 0) {
292 		printk("failed (err %d)\n", err);
293 		return err;
294 	}
295 	printk("success\n");
296 
297 	return 0;
298 }
299 
enable_cte_rx(void)300 static void enable_cte_rx(void)
301 {
302 	int err;
303 
304 	const struct bt_df_per_adv_sync_cte_rx_param cte_rx_params = {
305 		.max_cte_count = 5,
306 #if defined(CONFIG_BT_DF_CTE_RX_AOA)
307 		.cte_types = BT_DF_CTE_TYPE_ALL,
308 		.slot_durations = 0x2,
309 		.num_ant_ids = ARRAY_SIZE(ant_patterns),
310 		.ant_ids = ant_patterns,
311 #else
312 		.cte_types = BT_DF_CTE_TYPE_AOD_1US | BT_DF_CTE_TYPE_AOD_2US,
313 #endif /* CONFIG_BT_DF_CTE_RX_AOA */
314 	};
315 
316 	printk("Enable receiving of CTE...\n");
317 	err = bt_df_per_adv_sync_cte_rx_enable(adv_sync, &cte_rx_params);
318 	if (err != 0) {
319 		printk("failed (err %d)\n", err);
320 		return;
321 	}
322 	printk("success. CTE receive enabled.\n");
323 }
324 
scan_init(void)325 static int scan_init(void)
326 {
327 	printk("Scan callbacks register...");
328 	bt_le_scan_cb_register(&scan_callbacks);
329 	printk("success.\n");
330 
331 	printk("Periodic Advertising callbacks register...");
332 	bt_le_per_adv_sync_cb_register(&sync_callbacks);
333 	printk("success.\n");
334 
335 	return 0;
336 }
337 
scan_enable(void)338 static int scan_enable(void)
339 {
340 	struct bt_le_scan_param param = {
341 			.type       = BT_LE_SCAN_TYPE_ACTIVE,
342 			.options    = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
343 			.interval   = BT_GAP_SCAN_FAST_INTERVAL,
344 			.window     = BT_GAP_SCAN_FAST_WINDOW,
345 			.timeout    = 0U, };
346 	int err;
347 
348 	if (!scan_enabled) {
349 		printk("Start scanning...");
350 		err = bt_le_scan_start(&param, NULL);
351 		if (err != 0) {
352 			printk("failed (err %d)\n", err);
353 			return err;
354 		}
355 		printk("success\n");
356 		scan_enabled = true;
357 	}
358 
359 	return 0;
360 }
361 
scan_disable(void)362 static void scan_disable(void)
363 {
364 	int err;
365 
366 	printk("Scan disable...");
367 	err = bt_le_scan_stop();
368 	if (err != 0) {
369 		printk("failed (err %d)\n", err);
370 		return;
371 	}
372 	printk("Success.\n");
373 
374 	scan_enabled = false;
375 }
376 
main(void)377 int main(void)
378 {
379 	int err;
380 
381 	printk("Starting Connectionless Locator Demo\n");
382 
383 	printk("Bluetooth initialization...");
384 	err = bt_enable(NULL);
385 	if (err != 0) {
386 		printk("failed (err %d)\n", err);
387 	}
388 	printk("success\n");
389 
390 	scan_init();
391 
392 	scan_enabled = false;
393 	do {
394 		scan_enable();
395 
396 		printk("Waiting for periodic advertising...");
397 		per_adv_found = false;
398 		err = k_sem_take(&sem_per_adv, K_FOREVER);
399 		if (err != 0) {
400 			printk("failed (err %d)\n", err);
401 			return 0;
402 		}
403 		printk("success. Found periodic advertising.\n");
404 
405 		sync_wait = true;
406 		sync_terminated = false;
407 
408 		create_sync();
409 
410 		printk("Waiting for periodic sync...\n");
411 		err = k_sem_take(&sem_per_sync, K_MSEC(sync_create_timeout));
412 		if (err != 0 || sync_terminated) {
413 			if (err != 0) {
414 				printk("failed (err %d)\n", err);
415 			} else {
416 				printk("terminated\n");
417 			}
418 
419 			sync_wait = false;
420 
421 			err = delete_sync();
422 			if (err != 0) {
423 				return 0;
424 			}
425 
426 			continue;
427 		}
428 		printk("success. Periodic sync established.\n");
429 		sync_wait = false;
430 
431 		enable_cte_rx();
432 
433 		/* Disable scan to cleanup output */
434 		scan_disable();
435 
436 		printk("Waiting for periodic sync lost...\n");
437 		err = k_sem_take(&sem_per_sync_lost, K_FOREVER);
438 		if (err != 0) {
439 			printk("failed (err %d)\n", err);
440 			return 0;
441 		}
442 		printk("Periodic sync lost.\n");
443 	} while (true);
444 	return 0;
445 }
446