1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/bluetooth.h>
8 #include <zephyr/bluetooth/conn.h>
9 #include <zephyr/bluetooth/gap.h>
10 #include <zephyr/bluetooth/hci.h>
11 #include <zephyr/sys/util.h>
12 
13 #define NAME_LEN 30
14 
15 static K_SEM_DEFINE(sem_per_adv, 0, 1);
16 static K_SEM_DEFINE(sem_per_sync, 0, 1);
17 static K_SEM_DEFINE(sem_connected, 0, 1);
18 static K_SEM_DEFINE(sem_disconnected, 0, 1);
19 
20 static struct bt_conn *default_conn;
21 static bool per_adv_found;
22 static bt_addr_le_t per_addr;
23 static uint8_t per_sid;
24 
sync_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)25 static void sync_cb(struct bt_le_per_adv_sync *sync, struct bt_le_per_adv_sync_synced_info *info)
26 {
27 	struct bt_le_per_adv_sync_subevent_params params;
28 	uint8_t subevents[1];
29 	char le_addr[BT_ADDR_LE_STR_LEN];
30 	int err;
31 
32 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
33 	printk("Synced to %s with %d subevents\n", le_addr, info->num_subevents);
34 
35 	params.properties = 0;
36 	params.num_subevents = 1;
37 	params.subevents = subevents;
38 	subevents[0] = 0;
39 	err = bt_le_per_adv_sync_subevent(sync, &params);
40 	if (err) {
41 		printk("Failed to set subevents to sync to (err %d)\n", err);
42 	}
43 
44 	k_sem_give(&sem_per_sync);
45 }
46 
term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)47 static void term_cb(struct bt_le_per_adv_sync *sync,
48 		    const struct bt_le_per_adv_sync_term_info *info)
49 {
50 	char le_addr[BT_ADDR_LE_STR_LEN];
51 
52 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
53 
54 	printk("Sync terminated (reason %d)\n", info->reason);
55 }
56 
57 static struct bt_le_per_adv_response_params rsp_params;
58 
59 NET_BUF_SIMPLE_DEFINE_STATIC(rsp_buf, sizeof(bt_addr_le_t) + 2 * sizeof(uint8_t));
60 
recv_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)61 static void recv_cb(struct bt_le_per_adv_sync *sync,
62 		    const struct bt_le_per_adv_sync_recv_info *info, struct net_buf_simple *buf)
63 {
64 	int err;
65 	struct bt_le_oob oob;
66 	char addr_str[BT_ADDR_LE_STR_LEN];
67 
68 	if (default_conn) {
69 		/* Only respond with address if not already connected */
70 		return;
71 	}
72 
73 	if (buf && buf->len) {
74 		/* Respond with own address for the advertiser to connect to */
75 		net_buf_simple_reset(&rsp_buf);
76 
77 		rsp_params.request_event = info->periodic_event_counter;
78 		rsp_params.request_subevent = info->subevent;
79 		rsp_params.response_subevent = info->subevent;
80 		rsp_params.response_slot = 0;
81 
82 		err = bt_le_oob_get_local(BT_ID_DEFAULT, &oob);
83 		if (err) {
84 			printk("Failed to get OOB data (err %d)\n", err);
85 
86 			return;
87 		}
88 
89 		bt_addr_le_to_str(&oob.addr, addr_str, sizeof(addr_str));
90 		printk("Responding with own addr: %s\n", addr_str);
91 
92 		net_buf_simple_add_u8(&rsp_buf, sizeof(bt_addr_le_t));
93 		net_buf_simple_add_u8(&rsp_buf, BT_DATA_LE_BT_DEVICE_ADDRESS);
94 		net_buf_simple_add_mem(&rsp_buf, &oob.addr.a, sizeof(oob.addr.a));
95 		net_buf_simple_add_u8(&rsp_buf, oob.addr.type);
96 
97 		err = bt_le_per_adv_set_response_data(sync, &rsp_params, &rsp_buf);
98 		if (err) {
99 			printk("Failed to send response (err %d)\n", err);
100 		}
101 	} else if (buf) {
102 		printk("Received empty indication: subevent %d\n", info->subevent);
103 	} else {
104 		printk("Failed to receive indication: subevent %d\n", info->subevent);
105 	}
106 }
107 
108 static struct bt_le_per_adv_sync_cb sync_callbacks = {
109 	.synced = sync_cb,
110 	.term = term_cb,
111 	.recv = recv_cb,
112 };
113 
connected_cb(struct bt_conn * conn,uint8_t err)114 static void connected_cb(struct bt_conn *conn, uint8_t err)
115 {
116 	printk("Connected (err 0x%02X)\n", err);
117 
118 	if (err) {
119 		return;
120 	}
121 
122 	default_conn = bt_conn_ref(conn);
123 	k_sem_give(&sem_connected);
124 }
125 
disconnected_cb(struct bt_conn * conn,uint8_t reason)126 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
127 {
128 	bt_conn_unref(default_conn);
129 	default_conn = NULL;
130 
131 	printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
132 
133 	k_sem_give(&sem_disconnected);
134 }
135 
136 BT_CONN_CB_DEFINE(conn_cb) = {
137 	.connected = connected_cb,
138 	.disconnected = disconnected_cb,
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 
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)158 static void scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf)
159 {
160 	char name[NAME_LEN];
161 
162 	(void)memset(name, 0, sizeof(name));
163 	bt_data_parse(buf, data_cb, name);
164 
165 	if (strcmp(name, "PAwR conn sample")) {
166 		return;
167 	}
168 
169 	if (!per_adv_found && info->interval) {
170 		per_adv_found = true;
171 
172 		per_sid = info->sid;
173 		bt_addr_le_copy(&per_addr, info->addr);
174 
175 		k_sem_give(&sem_per_adv);
176 	}
177 }
178 
179 static struct bt_le_scan_cb scan_callbacks = {
180 	.recv = scan_recv,
181 };
182 
main(void)183 int main(void)
184 {
185 	struct bt_le_per_adv_sync_param sync_create_param;
186 	struct bt_le_per_adv_sync *sync;
187 	int err;
188 
189 	printk("Starting Periodic Advertising with Responses Synchronization Demo\n");
190 
191 	err = bt_enable(NULL);
192 	if (err) {
193 		printk("Bluetooth init failed (err %d)\n", err);
194 
195 		return 0;
196 	}
197 
198 	bt_le_scan_cb_register(&scan_callbacks);
199 	bt_le_per_adv_sync_cb_register(&sync_callbacks);
200 
201 	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
202 	if (err) {
203 		printk("failed (err %d)\n", err);
204 
205 		return 0;
206 	}
207 
208 	err = k_sem_take(&sem_per_adv, K_FOREVER);
209 	if (err) {
210 		printk("failed (err %d)\n", err);
211 
212 		return 0;
213 	}
214 	printk("Found periodic advertising.\n");
215 
216 	printk("Creating Periodic Advertising Sync");
217 	bt_addr_le_copy(&sync_create_param.addr, &per_addr);
218 	sync_create_param.options = 0;
219 	sync_create_param.sid = per_sid;
220 	sync_create_param.skip = 0;
221 	sync_create_param.timeout = 0xaa;
222 	err = bt_le_per_adv_sync_create(&sync_create_param, &sync);
223 	if (err) {
224 		printk("Failed to create sync (err %d)\n", err);
225 
226 		return 0;
227 	}
228 
229 	printk("Waiting for periodic sync\n");
230 	err = k_sem_take(&sem_per_sync, K_FOREVER);
231 	if (err) {
232 		printk("Failed (err %d)\n", err);
233 
234 		return 0;
235 	}
236 
237 	printk("Periodic sync established.\n");
238 
239 	err = bt_le_scan_stop();
240 	if (err) {
241 		printk("Failed to stop scanning (err %d)\n", err);
242 	}
243 
244 	printk("Stopped scanning\n");
245 
246 	do {
247 		err = k_sem_take(&sem_connected, K_FOREVER);
248 		if (err) {
249 			printk("failed (err %d)\n", err);
250 
251 			return 0;
252 		}
253 
254 		printk("Disconnecting\n");
255 
256 		err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
257 		if (err) {
258 			return 0;
259 		}
260 
261 		err = k_sem_take(&sem_disconnected, K_FOREVER);
262 		if (err) {
263 			printk("failed (err %d)\n", err);
264 
265 			return 0;
266 		}
267 	} while (true);
268 }
269