1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/att.h>
8 #include <zephyr/bluetooth/bluetooth.h>
9 #include <zephyr/bluetooth/conn.h>
10 #include <zephyr/bluetooth/gatt.h>
11 #include <zephyr/bluetooth/hci.h>
12 
13 #define NUM_RSP_SLOTS 5
14 #define NUM_SUBEVENTS 5
15 #define PACKET_SIZE   5
16 #define NAME_LEN      30
17 
18 static K_SEM_DEFINE(sem_connected, 0, 1);
19 static K_SEM_DEFINE(sem_discovered, 0, 1);
20 static K_SEM_DEFINE(sem_written, 0, 1);
21 static K_SEM_DEFINE(sem_disconnected, 0, 1);
22 
23 struct k_poll_event events[] = {
24 	K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY,
25 					&sem_connected, 0),
26 	K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY,
27 					&sem_disconnected, 0),
28 };
29 
30 static struct bt_uuid_128 pawr_char_uuid =
31 	BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
32 static uint16_t pawr_attr_handle;
33 static const struct bt_le_per_adv_param per_adv_params = {
34 	.interval_min = 0xFF,
35 	.interval_max = 0xFF,
36 	.options = 0,
37 	.num_subevents = NUM_SUBEVENTS,
38 	.subevent_interval = 0x30,
39 	.response_slot_delay = 0x5,
40 	.response_slot_spacing = 0x50,
41 	.num_response_slots = NUM_RSP_SLOTS,
42 };
43 
44 static struct bt_le_per_adv_subevent_data_params subevent_data_params[NUM_SUBEVENTS];
45 static struct net_buf_simple bufs[NUM_SUBEVENTS];
46 static uint8_t backing_store[NUM_SUBEVENTS][PACKET_SIZE];
47 
48 BUILD_ASSERT(ARRAY_SIZE(bufs) == ARRAY_SIZE(subevent_data_params));
49 BUILD_ASSERT(ARRAY_SIZE(backing_store) == ARRAY_SIZE(subevent_data_params));
50 
51 static uint8_t counter;
52 
request_cb(struct bt_le_ext_adv * adv,const struct bt_le_per_adv_data_request * request)53 static void request_cb(struct bt_le_ext_adv *adv, const struct bt_le_per_adv_data_request *request)
54 {
55 	int err;
56 	uint8_t to_send;
57 	struct net_buf_simple *buf;
58 
59 	to_send = MIN(request->count, ARRAY_SIZE(subevent_data_params));
60 
61 	for (size_t i = 0; i < to_send; i++) {
62 		buf = &bufs[i];
63 		buf->data[buf->len - 1] = counter++;
64 
65 		subevent_data_params[i].subevent =
66 			(request->start + i) % per_adv_params.num_subevents;
67 		subevent_data_params[i].response_slot_start = 0;
68 		subevent_data_params[i].response_slot_count = NUM_RSP_SLOTS;
69 		subevent_data_params[i].data = buf;
70 	}
71 
72 	err = bt_le_per_adv_set_subevent_data(adv, to_send, subevent_data_params);
73 	if (err) {
74 		printk("Failed to set subevent data (err %d)\n", err);
75 	} else {
76 		printk("Subevent data set %d\n", counter);
77 	}
78 }
79 
print_ad_field(struct bt_data * data,void * user_data)80 static bool print_ad_field(struct bt_data *data, void *user_data)
81 {
82 	ARG_UNUSED(user_data);
83 
84 	printk("    0x%02X: ", data->type);
85 	for (size_t i = 0; i < data->data_len; i++) {
86 		printk("%02X", data->data[i]);
87 	}
88 
89 	printk("\n");
90 
91 	return true;
92 }
93 
94 static struct bt_conn *default_conn;
95 
response_cb(struct bt_le_ext_adv * adv,struct bt_le_per_adv_response_info * info,struct net_buf_simple * buf)96 static void response_cb(struct bt_le_ext_adv *adv, struct bt_le_per_adv_response_info *info,
97 		     struct net_buf_simple *buf)
98 {
99 	if (buf) {
100 		printk("Response: subevent %d, slot %d\n", info->subevent, info->response_slot);
101 		bt_data_parse(buf, print_ad_field, NULL);
102 	}
103 }
104 
105 static const struct bt_le_ext_adv_cb adv_cb = {
106 	.pawr_data_request = request_cb,
107 	.pawr_response = response_cb,
108 };
109 
connected_cb(struct bt_conn * conn,uint8_t err)110 void connected_cb(struct bt_conn *conn, uint8_t err)
111 {
112 	printk("Connected (err 0x%02X)\n", err);
113 
114 	__ASSERT(conn == default_conn, "Unexpected connected callback");
115 
116 	if (err) {
117 		bt_conn_unref(default_conn);
118 		default_conn = NULL;
119 	}
120 }
121 
disconnected_cb(struct bt_conn * conn,uint8_t reason)122 void disconnected_cb(struct bt_conn *conn, uint8_t reason)
123 {
124 	printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
125 
126 	k_sem_give(&sem_disconnected);
127 }
128 
remote_info_available_cb(struct bt_conn * conn,struct bt_conn_remote_info * remote_info)129 void remote_info_available_cb(struct bt_conn *conn, struct bt_conn_remote_info *remote_info)
130 {
131 	/* Need to wait for remote info before initiating PAST */
132 	k_sem_give(&sem_connected);
133 }
134 
135 BT_CONN_CB_DEFINE(conn_cb) = {
136 	.connected = connected_cb,
137 	.disconnected = disconnected_cb,
138 	.remote_info_available = remote_info_available_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 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)158 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
159 			 struct net_buf_simple *ad)
160 {
161 	char addr_str[BT_ADDR_LE_STR_LEN];
162 	char name[NAME_LEN];
163 	int err;
164 
165 	if (default_conn) {
166 		return;
167 	}
168 
169 	/* We're only interested in connectable events */
170 	if (type != BT_GAP_ADV_TYPE_ADV_IND && type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
171 		return;
172 	}
173 
174 	(void)memset(name, 0, sizeof(name));
175 	bt_data_parse(ad, data_cb, name);
176 
177 	if (strcmp(name, "PAwR sync sample")) {
178 		return;
179 	}
180 
181 	if (bt_le_scan_stop()) {
182 		return;
183 	}
184 
185 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT,
186 				&default_conn);
187 	if (err) {
188 		printk("Create conn to %s failed (%u)\n", addr_str, err);
189 	}
190 }
191 
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)192 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
193 			     struct bt_gatt_discover_params *params)
194 {
195 	struct bt_gatt_chrc *chrc;
196 	char str[BT_UUID_STR_LEN];
197 
198 	printk("Discovery: attr %p\n", attr);
199 
200 	if (!attr) {
201 		return BT_GATT_ITER_STOP;
202 	}
203 
204 	chrc = (struct bt_gatt_chrc *)attr->user_data;
205 
206 	bt_uuid_to_str(chrc->uuid, str, sizeof(str));
207 	printk("UUID %s\n", str);
208 
209 	if (!bt_uuid_cmp(chrc->uuid, &pawr_char_uuid.uuid)) {
210 		pawr_attr_handle = chrc->value_handle;
211 
212 		printk("Characteristic handle: %d\n", pawr_attr_handle);
213 
214 		k_sem_give(&sem_discovered);
215 	}
216 
217 	return BT_GATT_ITER_STOP;
218 }
219 
write_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)220 static void write_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
221 {
222 	if (err) {
223 		printk("Write failed (err %d)\n", err);
224 
225 		return;
226 	}
227 
228 	k_sem_give(&sem_written);
229 }
230 
init_bufs(void)231 void init_bufs(void)
232 {
233 	for (size_t i = 0; i < ARRAY_SIZE(backing_store); i++) {
234 		backing_store[i][0] = ARRAY_SIZE(backing_store[i]) - 1;
235 		backing_store[i][1] = BT_DATA_MANUFACTURER_DATA;
236 		backing_store[i][2] = 0x59; /* Nordic */
237 		backing_store[i][3] = 0x00;
238 
239 		net_buf_simple_init_with_data(&bufs[i], &backing_store[i],
240 					      ARRAY_SIZE(backing_store[i]));
241 	}
242 }
243 
244 #define MAX_SYNCS (NUM_SUBEVENTS * NUM_RSP_SLOTS)
245 struct pawr_timing {
246 	uint8_t subevent;
247 	uint8_t response_slot;
248 } __packed;
249 
250 static uint8_t num_synced;
251 
main(void)252 int main(void)
253 {
254 	int err;
255 	struct bt_le_ext_adv *pawr_adv;
256 	struct bt_gatt_discover_params discover_params;
257 	struct bt_gatt_write_params write_params;
258 	struct pawr_timing sync_config;
259 
260 	init_bufs();
261 
262 	printk("Starting Periodic Advertising Demo\n");
263 
264 	/* Initialize the Bluetooth Subsystem */
265 	err = bt_enable(NULL);
266 	if (err) {
267 		printk("Bluetooth init failed (err %d)\n", err);
268 		return 0;
269 	}
270 
271 	/* Create a non-connectable advertising set */
272 	err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, &adv_cb, &pawr_adv);
273 	if (err) {
274 		printk("Failed to create advertising set (err %d)\n", err);
275 		return 0;
276 	}
277 
278 	/* Set periodic advertising parameters */
279 	err = bt_le_per_adv_set_param(pawr_adv, &per_adv_params);
280 	if (err) {
281 		printk("Failed to set periodic advertising parameters (err %d)\n", err);
282 		return 0;
283 	}
284 
285 	/* Enable Periodic Advertising */
286 	printk("Start Periodic Advertising\n");
287 	err = bt_le_per_adv_start(pawr_adv);
288 	if (err) {
289 		printk("Failed to enable periodic advertising (err %d)\n", err);
290 		return 0;
291 	}
292 
293 	printk("Start Extended Advertising\n");
294 	err = bt_le_ext_adv_start(pawr_adv, BT_LE_EXT_ADV_START_DEFAULT);
295 	if (err) {
296 		printk("Failed to start extended advertising (err %d)\n", err);
297 		return 0;
298 	}
299 
300 	while (num_synced < MAX_SYNCS) {
301 		/* Enable continuous scanning */
302 		err = bt_le_scan_start(BT_LE_SCAN_PASSIVE_CONTINUOUS, device_found);
303 		if (err) {
304 			printk("Scanning failed to start (err %d)\n", err);
305 			return 0;
306 		}
307 
308 		printk("Scanning successfully started\n");
309 
310 		/* Wait for either remote info available or involuntary disconnect */
311 		k_poll(events, ARRAY_SIZE(events), K_FOREVER);
312 		err = k_sem_take(&sem_connected, K_NO_WAIT);
313 		if (err) {
314 			printk("Disconnected before remote info available\n");
315 
316 			goto disconnected;
317 		}
318 
319 		err = bt_le_per_adv_set_info_transfer(pawr_adv, default_conn, 0);
320 		if (err) {
321 			printk("Failed to send PAST (err %d)\n", err);
322 
323 			goto disconnect;
324 		}
325 
326 		printk("PAST sent\n");
327 
328 		discover_params.uuid = &pawr_char_uuid.uuid;
329 		discover_params.func = discover_func;
330 		discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
331 		discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
332 		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
333 		err = bt_gatt_discover(default_conn, &discover_params);
334 		if (err) {
335 			printk("Discovery failed (err %d)\n", err);
336 
337 			goto disconnect;
338 		}
339 
340 		printk("Discovery started\n");
341 
342 		err = k_sem_take(&sem_discovered, K_SECONDS(10));
343 		if (err) {
344 			printk("Timed out during GATT discovery\n");
345 
346 			goto disconnect;
347 		}
348 
349 		sync_config.subevent = num_synced % NUM_SUBEVENTS;
350 		sync_config.response_slot = num_synced / NUM_SUBEVENTS;
351 		num_synced++;
352 
353 		write_params.func = write_func;
354 		write_params.handle = pawr_attr_handle;
355 		write_params.offset = 0;
356 		write_params.data = &sync_config;
357 		write_params.length = sizeof(sync_config);
358 
359 		err = bt_gatt_write(default_conn, &write_params);
360 		if (err) {
361 			printk("Write failed (err %d)\n", err);
362 			num_synced--;
363 
364 			goto disconnect;
365 		}
366 
367 		printk("Write started\n");
368 
369 		err = k_sem_take(&sem_written, K_SECONDS(10));
370 		if (err) {
371 			printk("Timed out during GATT write\n");
372 			num_synced--;
373 
374 			goto disconnect;
375 		}
376 
377 		printk("PAwR config written to sync %d, disconnecting\n", num_synced - 1);
378 
379 disconnect:
380 		/* Adding delay (2ms * interval value, using 2ms intead of the 1.25ms
381 		 * used by controller) to ensure sync is established before
382 		 * disconnection.
383 		 */
384 		k_sleep(K_MSEC(per_adv_params.interval_max * 2));
385 
386 		err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
387 		if (err != 0 && err != -ENOTCONN) {
388 			return 0;
389 		}
390 
391 disconnected:
392 		k_sem_take(&sem_disconnected, K_FOREVER);
393 
394 		bt_conn_unref(default_conn);
395 		default_conn = NULL;
396 	}
397 
398 	printk("Maximum numnber of syncs onboarded\n");
399 
400 	while (true) {
401 		k_sleep(K_SECONDS(1));
402 	}
403 
404 	return 0;
405 }
406