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/gatt.h>
10 #include <zephyr/bluetooth/uuid.h>
11 #include <zephyr/bluetooth/hci.h>
12 #include <zephyr/sys/util.h>
13 
14 #define NAME_LEN 30
15 
16 static K_SEM_DEFINE(sem_per_adv, 0, 1);
17 static K_SEM_DEFINE(sem_per_sync, 0, 1);
18 static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
19 
20 static struct bt_conn *default_conn;
21 static struct bt_le_per_adv_sync *default_sync;
22 static struct __packed {
23 	uint8_t subevent;
24 	uint8_t response_slot;
25 
26 } pawr_timing;
27 
sync_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)28 static void sync_cb(struct bt_le_per_adv_sync *sync, struct bt_le_per_adv_sync_synced_info *info)
29 {
30 	struct bt_le_per_adv_sync_subevent_params params;
31 	uint8_t subevents[1];
32 	char le_addr[BT_ADDR_LE_STR_LEN];
33 	int err;
34 
35 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
36 	printk("Synced to %s with %d subevents\n", le_addr, info->num_subevents);
37 
38 	default_sync = sync;
39 
40 	params.properties = 0;
41 	params.num_subevents = 1;
42 	params.subevents = subevents;
43 	subevents[0] = pawr_timing.subevent;
44 
45 	err = bt_le_per_adv_sync_subevent(sync, &params);
46 	if (err) {
47 		printk("Failed to set subevents to sync to (err %d)\n", err);
48 	} else {
49 		printk("Changed sync to subevent %d\n", subevents[0]);
50 	}
51 
52 	k_sem_give(&sem_per_sync);
53 }
54 
term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)55 static void term_cb(struct bt_le_per_adv_sync *sync,
56 		    const struct bt_le_per_adv_sync_term_info *info)
57 {
58 	char le_addr[BT_ADDR_LE_STR_LEN];
59 
60 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
61 
62 	printk("Sync terminated (reason %d)\n", info->reason);
63 
64 	default_sync = NULL;
65 
66 	k_sem_give(&sem_per_sync_lost);
67 }
68 
print_ad_field(struct bt_data * data,void * user_data)69 static bool print_ad_field(struct bt_data *data, void *user_data)
70 {
71 	ARG_UNUSED(user_data);
72 
73 	printk("    0x%02X: ", data->type);
74 	for (size_t i = 0; i < data->data_len; i++) {
75 		printk("%02X", data->data[i]);
76 	}
77 
78 	printk("\n");
79 
80 	return true;
81 }
82 
83 int bt_le_per_adv_set_response_data(struct bt_le_per_adv_sync *per_adv_sync,
84 				    const struct bt_le_per_adv_response_params *params,
85 				    const struct net_buf_simple *data);
86 
87 static struct bt_le_per_adv_response_params rsp_params;
88 
89 NET_BUF_SIMPLE_DEFINE_STATIC(rsp_buf, 247);
90 
recv_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)91 static void recv_cb(struct bt_le_per_adv_sync *sync,
92 		    const struct bt_le_per_adv_sync_recv_info *info, struct net_buf_simple *buf)
93 {
94 	int err;
95 
96 	if (buf && buf->len) {
97 		/* Echo the data back to the advertiser */
98 		net_buf_simple_reset(&rsp_buf);
99 		net_buf_simple_add_mem(&rsp_buf, buf->data, buf->len);
100 
101 		rsp_params.request_event = info->periodic_event_counter;
102 		rsp_params.request_subevent = info->subevent;
103 		/* Respond in current subevent and assigned response slot */
104 		rsp_params.response_subevent = info->subevent;
105 		rsp_params.response_slot = pawr_timing.response_slot;
106 
107 		printk("Indication: subevent %d, responding in slot %d\n", info->subevent,
108 		       pawr_timing.response_slot);
109 		bt_data_parse(buf, print_ad_field, NULL);
110 
111 		err = bt_le_per_adv_set_response_data(sync, &rsp_params, &rsp_buf);
112 		if (err) {
113 			printk("Failed to send response (err %d)\n", err);
114 		}
115 	} else if (buf) {
116 		printk("Received empty indication: subevent %d\n", info->subevent);
117 	} else {
118 		printk("Failed to receive indication: subevent %d\n", info->subevent);
119 	}
120 }
121 
122 static struct bt_le_per_adv_sync_cb sync_callbacks = {
123 	.synced = sync_cb,
124 	.term = term_cb,
125 	.recv = recv_cb,
126 };
127 
128 static const struct bt_uuid_128 pawr_svc_uuid =
129 	BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0));
130 static const struct bt_uuid_128 pawr_char_uuid =
131 	BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
132 
write_timing(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)133 static ssize_t write_timing(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
134 			    uint16_t len, uint16_t offset, uint8_t flags)
135 {
136 	if (offset) {
137 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
138 	}
139 
140 	if (len != sizeof(pawr_timing)) {
141 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
142 	}
143 
144 	memcpy(&pawr_timing, buf, len);
145 
146 	printk("New timing: subevent %d, response slot %d\n", pawr_timing.subevent,
147 	       pawr_timing.response_slot);
148 
149 	struct bt_le_per_adv_sync_subevent_params params;
150 	uint8_t subevents[1];
151 	int err;
152 
153 	params.properties = 0;
154 	params.num_subevents = 1;
155 	params.subevents = subevents;
156 	subevents[0] = pawr_timing.subevent;
157 
158 	if (default_sync) {
159 		err = bt_le_per_adv_sync_subevent(default_sync, &params);
160 		if (err) {
161 			printk("Failed to set subevents to sync to (err %d)\n", err);
162 		} else {
163 			printk("Changed sync to subevent %d\n", subevents[0]);
164 		}
165 	} else {
166 		printk("Not synced yet\n");
167 	}
168 
169 	return len;
170 }
171 
172 BT_GATT_SERVICE_DEFINE(pawr_svc, BT_GATT_PRIMARY_SERVICE(&pawr_svc_uuid.uuid),
173 		       BT_GATT_CHARACTERISTIC(&pawr_char_uuid.uuid, BT_GATT_CHRC_WRITE,
174 					      BT_GATT_PERM_WRITE, NULL, write_timing,
175 					      &pawr_timing));
176 
connected(struct bt_conn * conn,uint8_t err)177 void connected(struct bt_conn *conn, uint8_t err)
178 {
179 	printk("Connected, err 0x%02X %s\n", err, bt_hci_err_to_str(err));
180 
181 	if (err) {
182 		default_conn = NULL;
183 
184 		return;
185 	}
186 
187 	default_conn = bt_conn_ref(conn);
188 }
189 
disconnected(struct bt_conn * conn,uint8_t reason)190 void disconnected(struct bt_conn *conn, uint8_t reason)
191 {
192 	bt_conn_unref(default_conn);
193 	default_conn = NULL;
194 
195 	printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
196 }
197 
198 BT_CONN_CB_DEFINE(conn_cb) = {
199 	.connected = connected,
200 	.disconnected = disconnected,
201 };
202 
203 static const struct bt_data ad[] = {
204 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
205 };
206 
main(void)207 int main(void)
208 {
209 	struct bt_le_per_adv_sync_transfer_param past_param;
210 	int err;
211 
212 	printk("Starting Periodic Advertising with Responses Synchronization Demo\n");
213 
214 	err = bt_enable(NULL);
215 	if (err) {
216 		printk("Bluetooth init failed (err %d)\n", err);
217 
218 		return 0;
219 	}
220 
221 	bt_le_per_adv_sync_cb_register(&sync_callbacks);
222 
223 	past_param.skip = 1;
224 	past_param.timeout = 1000; /* 10 seconds */
225 	past_param.options = BT_LE_PER_ADV_SYNC_TRANSFER_OPT_NONE;
226 	err = bt_le_per_adv_sync_transfer_subscribe(NULL, &past_param);
227 	if (err) {
228 		printk("PAST subscribe failed (err %d)\n", err);
229 
230 		return 0;
231 	}
232 
233 	do {
234 		err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
235 		if (err && err != -EALREADY) {
236 			printk("Advertising failed to start (err %d)\n", err);
237 
238 			return 0;
239 		}
240 
241 		printk("Waiting for periodic sync...\n");
242 		err = k_sem_take(&sem_per_sync, K_SECONDS(10));
243 		if (err) {
244 			printk("Timed out while synchronizing\n");
245 
246 			continue;
247 		}
248 
249 		printk("Periodic sync established.\n");
250 
251 		err = k_sem_take(&sem_per_sync_lost, K_FOREVER);
252 		if (err) {
253 			printk("failed (err %d)\n", err);
254 
255 			return 0;
256 		}
257 
258 		printk("Periodic sync lost.\n");
259 	} while (true);
260 
261 	return 0;
262 }
263