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, ¶ms);
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